发布

  • [OPIK-6852] [BE] Agent Insights jobs CRUD (#7087)

    frostbyte_neo 发布于 2026-06-18 11:05:41 +00:00

    • [OPIK-6852] [BE] Agent Insights jobs CRUD

    Per-(workspace, project) configuration for the Agent Insights report in
    apps/opik-backend, built from scratch mirroring the Ollie daily-report feature
    (ReportPreferenceDAO / ReportService / ReportsResource).

    • agent_insights_jobs table (migration 000082) + AgentInsightsJobDAO
    • AgentInsightsJobService: enable (upsert -> enabled + immediate first run), get,
      disable (status flip, never delete), findAllEnabled (consumed by OPIK-6853 cron)
    • AgentInsightsJobResource at /v1/private/agent-insights/jobs:
      POST /jobs (201 first enable / 200 if existed), GET /jobs?project_id (404 if none),
      POST /jobs/disable (204 / 404). workspace_id always from the auth context.
    • DAO integration test + JAX-RS resource tests + trigger test (idempotency,
      workspace isolation, 404 paths, immediate-run-once).

    The immediate run on enable goes through AgentInsightsReportClient, bound to a
    no-op default; the real Platform-BE client is OPIK-6854's deliverable and replaces
    this binding (the immediate run is inert until then).

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Address review: plural resource naming + validation test + Location header
    • Rename AgentInsightsJobResource/Test -> AgentInsightsJobsResource(Test) per the
      opik-backend plural-resource naming convention
    • Document the Location header on the 201 response
    • Add a 400 test for enable with a missing project_id (AgentInsightsJobsResourceTest)

    Not applied: deriving created from the upsert affected-row count. The project's MySQL
    connector returns matched-not-changed rows, so ON DUPLICATE KEY UPDATE reports 1 on the
    duplicate path too, breaking the 201-vs-200 distinction. Kept the in-transaction pre-read.

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Fix missing-project_id test to exercise the real validation path (422)

    The previous test posted the JSON string "{}" via Entity.json(String), which the client
    serializes Jackson-quoted -> a malformed-body 400, not the @NotNull validation. Post a
    proper empty object (Map.of()) so project_id is null and assert 422 (opik's bean-validation
    status), genuinely covering the missing-project_id case.

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Split scheduler endpoints: create / PATCH update / trigger

    Reshape the agent-insights jobs resource per review feedback:

    • POST /jobs now only CREATES (idempotent upsert, 201/200) and no longer triggers an
      immediate run
    • POST /jobs/disable -> PATCH /jobs?project_id= : partial update of any mutable field
      (today: status), returns the updated job (200), 404 when absent
    • new POST /jobs/trigger {project_id} -> 202 : runs the immediate report on demand
      (404 when the job doesn't exist)

    Service: create / update / triggerNow; DAO disable -> general updateStatus; new
    AgentInsightsJobUpdate request DTO. Tests reworked accordingly (resource 8, dao 6,
    trigger 1; the trigger test now asserts create does NOT fire and the trigger endpoint does).

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Drop the agent_insights_jobs status index (review)

    Per review: the status index isn't needed (low-cardinality enum; findAllEnabled scans
    the small enabled set anyway).

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Renumber migration 000082 -> 000083 (prefix conflict with main)

    main merged 000082_create_agent_insights_issues_tables.sql while this PR was open, so the
    jobs-table migration now collides on the 000082 prefix. Renumber to the next free prefix
    (000083) and update the changeset id. As a new changeset it also sidesteps the in-place
    checksum concern on the deployed PR env.

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Review: project_id as path var, create-409, insert-only DAO

    Address Thiago/Boris review (CRUD-side):

    • project_id is now a path variable: POST/GET/PATCH/POST-trigger on /jobs/{projectId}
      (drops the request body / query param; removes AgentInsightsJobRequest)
    • create() returns 201 and throws 409 (EntityAlreadyExistsException) if a job already
      exists for the (workspace, project) — no longer an upsert
    • DAO enable -> create: plain INSERT (insert-only; duplicate violates the unique key)
    • AgentInsightsJob is response-only: project_id marked @Schema(requiredMode = REQUIRED)
    • trimmed verbose comments

    Scheduler bits (trigger, findAllEnabled, last_triggered_at/markTriggered, report client)
    intentionally kept here for now; auth stays PROJECT_DATA_VIEW (documented in review reply).
    Tests reworked: resource 7, dao 6 (create insert-only + duplicate-throws), trigger 1.

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Remove scheduler-only DB bits (defer to OPIK-6853)

    Per review, drop the bits that belong to the cron PR (OPIK-6853), not CRUD:

    • findAllEnabled (DAO + Service) and the EnabledJob projection
    • markTriggered (DAO) and its call in triggerImmediate
    • last_triggered_at (migration column + AgentInsightsJob field)

    The manual trigger endpoint stays (fire-and-forget); it no longer records a run time.
    Tests trimmed accordingly (DAO 4, resource 7, trigger 1).

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] Defer trigger execution to OPIK-6853; drop report client + DAO/trigger tests

    Address the remaining Thiago/Boris review:

    • triggerNow no longer runs synchronously (Thiago's blocker: no inline/unbounded work).
      It validates the job exists (404) and is a no-op placeholder with a TODO(OPIK-6853) to
      enqueue the run on a bounded async queue. Endpoint kept, returns 202.
    • Remove AgentInsightsReportClient + NoopAgentInsightsReportClient (no longer used here;
      the real client + queue land in OPIK-6853).
    • Remove AgentInsightsJobDAOTest and AgentInsightsJobTriggerTest — the resource
      integration test (AgentInsightsJobsResourceTest) covers create/get/update/trigger.
    • AgentInsightsJob.projectId: @Schema(READ_ONLY + REQUIRED) (response-only model).

    #7087 is now pure CRUD; scheduler/trigger execution belongs to OPIK-6853 (#7105).

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] create(): race-safe 409 via EntityConstraintHandler + reuse validateProjectIdExists

    Address Baz:

    • map the unique-key violation to 409 with EntityConstraintHandler.handle(...).withError(...)
      so concurrent creates return 409 instead of a 500 (drops the racy check-then-insert)
    • reuse ProjectService.validateProjectIdExists(projectId, workspaceId) for the 404 instead
      of a hand-rolled findByIds().isEmpty() check

    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

    • [OPIK-6852] De-reactify AgentInsightsJobService — plain sync state-DB calls

    Per Boris review: jobs CRUD hits MySQL (state DB), not ClickHouse, so the reactor
    wrapping (Mono.fromCallable + subscribeOn(boundedElastic) + resource .block()/
    .contextWrite) was unnecessary ceremony. Return plain types and call
    transactionTemplate.inTransaction(...) directly, matching AlertService/PromptService.

    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com


    Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com

    下载附件