Resolve AI Catalog agent references at flow save time

To run the whole chain locally: gitlab poc/ai-catalog-subagents-stack, ai-assist poc/ai-catalog-subagents, gitlab-lsp poc/ai-catalog-subagents. Each branch stacks the open MRs of that repository plus the pins the chain waits on (Workhorse gopb, the 0.1.70 Node client, a 9.25.0-poc Duo CLI build).

What does this MR do and why?

Save-time resolution for flow YAML include field references (which is MR 2 and 4 of the plan on #628153 (closed)).

For each reference:

  • The item and pinned version must exist.
  • The saving user needs read_ai_catalog_item.
  • The item's visibility must permit the reference (private: same project, restricted: same root group).
  • The reference count is checked against ReferenceLimits.
  • Resolved references are written as ItemVersion to ItemVersion dependency rows with the exact pin

The save-time methods live in a new Ai::Catalog::Concerns::AgentReferences concern instead of FlowHelper, which !256807 (merged) deleted as dead code. Turned out we could have kept it, but this may be more semantically precise 🤞🏻

User docs for this behaviour are in !259499, which can merge on its own.

References

How to set up and validate locally

bundle exec rspec ee/spec/services/ai/catalog/flows/reference_resolver_spec.rb \
  ee/spec/services/ai/catalog/flows/create_service_spec.rb \
  ee/spec/services/ai/catalog/flows/update_service_spec.rb

Database

persist_agent_references replaces a flow version's dependency rows on every save: one relation delete_all, then one bulk_insert!. Both are bounded by ReferenceLimits at 20 rows per flow version.

Plans from postgres.ai (gitlab-production-main), session #59200. The table is empty in production (the feature is unreleased), so the clone was prepared first: the schema from !256807 (merged) (now merged, at the time not yet) was applied via exec (the dependency_version_id column, both indexes, and the composite FK), then 10,000 rows were seeded as 500 flow versions with 20 agent references each, the per-flow ceiling. The applied schema matches what !256807 (merged) merged, so the plans hold for master.

DELETE, 20 rows:

DELETE FROM "ai_catalog_item_version_dependencies"
WHERE "ai_catalog_item_version_dependencies"."ai_catalog_item_version_id" = 1

0.7 ms, 27 buffer hits: index scan on idx_ai_catalog_item_version_dependencies_version_and_dependency, actual rows=20.

INSERT, 20 rows (one multi-row statement from bulk_insert!):

INSERT INTO "ai_catalog_item_version_dependencies"
  ("ai_catalog_item_version_id","dependency_id","dependency_version_id","organization_id")
VALUES (1030784, 1015864, 1030734, 1), ... 20 tuples total

4.3 ms, 371 buffer hits: Values Scan plus the four FK trigger checks, 20 calls each.

Edited by Angus Ryer

Merge request reports

Loading
Loading