Commit Graph

78 Commits

Author SHA1 Message Date
Viren Baraiya 98a0a7320f Fix references to design docs removed from the repo (#1555) 2026-08-20 15:54:39 -07:00
Viren Baraiya 58b461bf34 Agent docs (#1406) 2026-08-20 12:52:15 -07:00
Nicholas Cole 5c16c85322 feat(agents): aggregate execution token usage (#1484) 2026-08-05 11:39:04 -07:00
Shailesh Padave 71ca791c6c fix: self-hosted models — base_url NPE + OPENAI_BASE_URL silently ignored (#1468) 2026-08-04 09:15:43 -07:00
Viren Baraiya b074897f39 Remove sqlite vector (#1455) 2026-08-02 19:54:39 -07:00
Manan Bhatt 0f33f5b8b5 fix(ai): surface agentType collisions; cover the external client wiring
Closes the two loose ends left in this PR so the release does not need a
follow-up.

A2AWorkers indexed clients with agentClients.put(agentType, client), so two
clients claiming one type last-one-wins on discovery order and one disappears
silently. Before #1358 the single-client injection raised
NoUniqueBeanDefinitionException, so this traded a loud failure for an invisible
one — and defaulting agentType() to "conductor" earlier in this PR makes the
collision easier to hit, since an implementation that forgets to override now
matches the built-in client. Keep the first registration and log which client was
ignored, on both the Spring and SDK paths.

Adds ExternalAgentClientGatingTest over the two external clients, which had no
coverage at all: absent when the AI integration is off, registered together with
CredentialResolutionService when on, reporting their own agent types with no
credentials configured anywhere, and — the regression that motivated the
qualifier — Azure Foundry still resolving its OkHttpClient when a second
OkHttpClient bean exists. Verified the last case fails without the qualifier
(NoUniqueBeanDefinitionException: found 2) and passes with it.

The dropped "AGENT (conductor) requires 'name'" validation from #1358 is
deliberate and stays dropped: the delegate now also serves bedrock and
azure-foundry, whose identity comes from rawConfig/assistantId rather than a
name, which is why the remaining messages were genericised to "AGENT requires
'prompt'".

Verified: :conductor-agentspan:test 560 tests, :conductor-ai:test 750 tests, 0
failures; :conductor-test-harness:compileTestJava clean; spotless clean.
2026-07-29 15:39:41 +05:30
Manan Bhatt dd3765d9c6 Merge branch 'main' into fix/restore-agentspan-embedded-gate 2026-07-29 13:55:01 +05:30
Manan Bhatt f36cbe29e2 fix(ai): give ConductorAgentClient.agentType() a default
#1358 added agentType() as an abstract method on the public
ConductorAgentClient interface. That is a source- and binary-breaking change in
a minor release: every external implementation — SDK-backed worker adapters and
any host supplying its own agent control plane — stops compiling until it adds
the method, even though "conductor" is the only value such an implementation
could reasonably return.

Default it to "conductor" so those implementations keep working, and let clients
backing another runtime override. The literal is inlined rather than referencing
A2AService.AGENT_TYPE_CONDUCTOR (same value, asserted by the javadoc) to keep
this boundary interface import-free, as it is today.

The in-tree implementations are unaffected: ServiceConductorAgentClient already
returns the same value explicitly, and the bedrock / azure-foundry clients
override.

Verified: ./gradlew :conductor-ai:test :conductor-agentspan:test — 0 failures.
Also confirmed downstream: with this plus the client gating in this PR, an
embedding host that previously needed both a component-scan workaround and its
own agentType() override compiles and boots clean with neither, under AI enabled
and disabled.
2026-07-29 13:17:20 +05:30
Shailesh Padave de6da295fc Merge branch 'main' into feature/a2a-expose-all 2026-07-29 11:49:02 +05:30
Manan Bhatt 393891501c Merge branch 'main' into feature/external-agent-clients 2026-07-29 10:38:18 +05:30
Shailesh Padave a9e6316ca1 Merge branch 'main' into feature/a2a-expose-all 2026-07-29 01:22:16 +05:30
nicholascole 1e0072f97d refactor(ai): consolidate agent integration configuration 2026-07-28 11:53:15 -07:00
Shailesh Padave d2672e93c3 Merge branch 'main' into feature/a2a-expose-all 2026-07-28 22:07:40 +05:30
Shailesh Padave b79f9b20dc fix(a2a): wrap long javadoc lines to pass spotlessJavaCheck
google-java-format enforces a 100-char line limit; the single-line
/** … */ comments added for basePath/agentBasePath fields and the
agentUrl helper exceeded that limit.
2026-07-28 15:10:21 +05:30
Shailesh Padave a3a6198a8a feat(a2a): expose native Conductor agents as A2A agents at /api/a2a/agent
Adds A2ANativeAgentFacade and A2AAgentServerResource in the agentspan module
to mirror the workflow-side A2A server at /api/a2a/agent/{name}. Native agents
are listed via AgentService, started via AgentService.start(), and execution
tracking reuses the same workflow-state mapping as the workflow side.

Closes #1407
2026-07-28 12:49:27 +05:30
Shailesh Padave eb3832bb04 refactor(a2a): move workflow agent endpoints to /api/a2a/workflow
Renames the default A2A server base path from /a2a to /api/a2a/workflow
to align with Conductor's /api prefix convention and reserve /api/a2a/agent
for future native-agent exposure.
2026-07-28 12:14:49 +05:30
Ling-Sen Peng 3a593c4439 test(ai): let Anthropic budget failures fail integration tests 2026-07-24 09:54:33 -07:00
Ling-Sen Peng e8584b731d test(ai): skip Anthropic media test on usage cap 2026-07-23 13:56:21 -07:00
Ling-Sen Peng 20205dd68c fix(test): align Grok key env alias; refresh stale live-test model ids
- conductor.ai.grok.api-key now falls back XAI_API_KEY -> GROK_API_KEY,
  so the org secret (GROK_API_KEY, which the tests already gate on) also
  configures the server-side provider; XAI_API_KEY still wins when set.
- BedrockTest.IntegrationTests chat test: legacy bare claude-3-haiku id
  -> current us. Haiku 4.5 inference profile (same two failures the
  media test hit live: profile required, Legacy model blocked).
- GeminiVertexTest Vertex chat test: retired gemini-1.5-flash ->
  gemini-2.5-flash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:37:31 -07:00
Ling-Sen Peng 6500924f2d fix(test): use current claude-haiku-4-5 inference profile for Bedrock media test
claude-3-haiku is marked Legacy by the provider and Bedrock rejects it
after 30 days of inactivity ("Access denied. This Model is marked by
provider as Legacy"). Use the active Haiku 4.5 US inference profile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:34:23 -07:00
Ling-Sen Peng 588471fb44 fix(test): use Bedrock inference-profile model id in media test
The bearer-token auth works, but Bedrock rejects the bare model id for
on-demand throughput ("Retry your request with the ID or ARN of an
inference profile") — use the us. cross-region profile id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:26:34 -07:00
Ling-Sen Peng 597e49838b test(ai): add OpenAI, Azure OpenAI, and Bedrock media integration tests
- OpenAITest / AzureOpenAITest: testChatCompletionWithImageMedia on
  gpt-4o-mini — both ride OpenAIResponsesChatModel's input_image path
  (pre-existing support; this locks it live at each endpoint).
- BedrockTest: new BearerIntegrationTests nest gated on
  AWS_BEARER_TOKEN_BEDROCK (the dedicated org secret, matching the
  server's conductor.ai.bedrock.bearerToken binding) — kept separate
  from the access-key nest because the CI AWS keys are provisioned for
  artifact publishing and may lack Bedrock permissions. Media flows via
  Spring AI Converse image blocks; model claude-3-haiku (vision-capable,
  same id as the existing bedrock tests).
- Workflow wires the new secrets (OPENAI, AZURE_OPENAI key+endpoint,
  AWS_BEARER_TOKEN_BEDROCK, AWS_REGION).

Verified locally: Anthropic and OpenAI pass against the live APIs;
keyless providers skip. Azure/Bedrock get their first live run on this
PR's workflow run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:18:43 -07:00
Ling-Sen Peng fd67c4fae4 test(ai): live media-input integration tests per provider + CI workflow
Adds testChatCompletionWithImageMedia to the key-gated IntegrationTests
nests of Anthropic (#1238), Grok and Perplexity (#1243), and a new
GEMINI_API_KEY-gated ApiKeyIntegrationTests nest for Gemini (#1241,
AI-Studio mode — the mode the server uses with conductor.ai.gemini.api-key),
complementing the existing Cohere test (#1246). Each sends the repo's
token image (MELON7391) to a vision model and asserts transcription —
a pass proves the adapter attached the media; the token cannot be guessed.

Vision models verified against provider docs: claude-haiku-4-5,
gemini-2.5-flash, grok-4.5 (docs.x.ai image understanding), sonar
(Perplexity image attachments, data-URI image_url).

New workflow llm-media-integration.yml runs the suite on GitHub with the
org-level provider secrets (weekly cron + workflow_dispatch + PR runs on
workflow changes). Tests self-skip per absent key (COHERE_API_KEY is not
configured today), so the run stays green with per-provider status.

Verified locally: Anthropic passes against the live API; keyless
providers skip. Full gemini/grok/perplexity verification happens on the
PR's own workflow run via org secrets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:00:31 -07:00
Shailesh Padave 430c1b1a34 fix(a2a): initialize agentClients map lazily to avoid Spring circular dependency
@PostConstruct ran too early: ServiceConductorAgentClient could not initialize
because its chain (AgentService → WorkflowExecutorOps → SystemTaskRegistry
@DependsOn workerTaskAnnotationScanner → A2AWorkers) creates a circular
initialization dependency with A2AWorkers still in creation.

Replace @PostConstruct registerAgentClients() with a clients() accessor that
performs a double-checked lazy load on first AGENT/CANCEL_AGENT task execution,
at which point all Spring beans are fully initialized.
2026-07-21 11:05:40 +05:30
Shailesh Padave 3d414de0b5 feat(a2a): add expose-all flag to auto-expose all workflows as A2A agents
Add conductor.a2a.server.expose-all=true property so every registered
workflow is automatically available as an A2A agent without requiring
per-workflow metadata (a2a.enabled=true) or explicit exposedWorkflows
config. Useful for development and single-tenant deployments.
2026-07-20 23:34:45 +05:30
Shailesh Padave 99fea7b03c spotless apply 2026-07-20 19:30:45 +05:30
Shailesh Padave 83d6b330bf Fix test compilation errors after A2AWorkers and ConductorAgentClient changes
- Add agentType() to UnusedConductorAgentClient and FakeConductorAgentClient
  (required by the new abstract method on the interface)
- Wrap single ConductorAgentClient args in List.of() in all test constructors
  (non-Spring A2AWorkers constructor now takes List<ConductorAgentClient>)
- Stub conductorAgentClient.agentType() to "conductor" in A2ACancelWorkerTest
  so the mock is registered under the expected routing key
2026-07-20 19:12:27 +05:30
Shailesh Padave 4fe55b3c8f fix: remove name-required guard from ConductorAgentDelegate for external agents
The 'name' field is conductor-internal (identifies which embedded agent
workflow to call). External agents (Azure Foundry, Bedrock) don't use it
and would fail unnecessarily. Each client's startAgent() already validates
its own required fields. Kept the prompt check which is universal.
2026-07-20 17:44:11 +05:30
Shailesh Padave 40406974f1 fix: break circular dependency in A2AWorkers Spring wiring
The constructor injection of List<ConductorAgentClient> caused a cycle:
A2AWorkers → ServiceConductorAgentClient → AgentService →
WorkflowServiceImpl → WorkflowExecutorOps → A2AWorkers.

Fix: Spring constructor now takes ApplicationContext instead of the
client list. A @PostConstruct method populates the agentClients map via
ApplicationContext.getBeansOfType() after all beans are constructed.
Non-Spring constructor (SDK/tests) is unchanged and still accepts a list.
2026-07-20 17:34:29 +05:30
Shailesh Padave 423b8c783f fix: correct SdkBytes API usage in BedrockAgentClient and remove stale imports
SdkBytes.bytes() does not exist; use asUtf8String() directly on the
SdkBytes returned by PayloadPart.bytes(). Also removed the leftover
ConditionalOnProperty imports from BedrockAgentClient and
AzureFoundryAgentClient (annotations were removed earlier).
2026-07-20 17:03:39 +05:30
Shailesh Padave 5ca6ad9581 Route external agent clients by agentType — no feature flags needed
Add agentType() to ConductorAgentClient interface so each implementation
self-declares its routing key. A2AWorkers now takes List<ConductorAgentClient>,
builds a map at startup, and routes by agentType on the request — no
conditional logic, no @ConditionalOnProperty flags.

- conductor → ServiceConductorAgentClient (existing)
- bedrock   → BedrockAgentClient (new)
- azure-foundry → AzureFoundryAgentClient (new)
- anything else → A2A remote path (existing fallback)

Adding a new platform is now: implement ConductorAgentClient, declare
agentType(), register as a Spring bean — nothing else changes.
2026-07-20 15:58:12 +05:30
Shailesh Padave 9e8b2f5430 Add external agent client infrastructure for Bedrock and Azure Foundry
- TokenProvider interface + StaticTokenProvider + OAuthTokenProvider (with
  Entra ID factory, thread-safe token cache with 60s refresh buffer)
- AgentCredentialConfig model with Platform enum for extensibility
- credentialRef field on ConductorAgentStartRequest so callers can point
  to a named secret without embedding credentials in the request
- BedrockAgentClient: streams Bedrock Agent Runtime responses, buffers
  output, surfaces returnControl events as WAITING state; falls back to
  default AWS credential chain when no credentialRef is set
- AzureFoundryAgentClient: A2A over HTTP with Entra ID bearer token auth;
  per-execution state map so getAgentStatus/respond/cancelAgent all have
  endpoint context without callers needing to pass it again
- KnownProviderEnvVars: added AZURE_CLIENT_ID/SECRET/TENANT_ID,
  AZURE_FOUNDRY_ENDPOINT, BEDROCK_AGENT_ID/ALIAS_ID for auto-seeding
2026-07-20 15:37:15 +05:30
Viren Baraiya 3ea601884d Enhances A2A/AgentSpan execution (#1356) 2026-07-20 00:04:20 -07:00
Shailesh Padave 92d1aec166 feat(a2a): backward compat with A2A v0.2 clients (tasks/send, /rpc alias) (#1341) 2026-07-19 11:09:08 -07:00
Viren Baraiya 86b58855d5 fixes (#1354) 2026-07-17 22:12:52 -07:00
Viren Baraiya 80156f58ff Task cancel contract (#1342) 2026-07-17 12:27:07 -07:00
Viren Baraiya c85fa0fb4f support for agent task and other UI fixes 2026-07-17 00:24:44 -07:00
Viren Baraiya 0110401072 Resolve #1286: Support conductor agents in the AGENT tasks (#1288) 2026-07-15 15:50:17 -07:00
Viren Baraiya 182d0d966b Add Agentspan and Agentic Workflows to Conductor 2026-07-13 11:55:29 -07:00
Viren Baraiya e3b117514a Update LLMHelper.java 2026-07-11 22:30:23 -07:00
Aarish Alam 1370f450ca feat: add LiteLLM as AI gateway provider (#1249) 2026-07-09 20:24:19 -07:00
ling-senpeng13 06c44ee4ff fix(ai): attach user media for Cohere (vision) requests (#1246) 2026-07-09 20:23:20 -07:00
ling-senpeng13 c19c1fdbc2 fix(ai): migrate HuggingFace to OpenAI-compatible router (chat + multimodal) (#1245) 2026-07-09 20:22:09 -07:00
ling-senpeng13 9a2e7a4ee5 fix(ai): attach user media for OpenAI-compatible providers (Grok, Perplexity) (#1243) 2026-07-09 20:21:56 -07:00
ling-senpeng13 65e34e09cc fix(ai): attach user media to Gemini requests (#1241) 2026-07-09 20:21:42 -07:00
vishesh-orkes b2283b725d fix(ai): normalize OpenAI base URL to always include /v1
The OpenAI provider's embeddings/responses/image/speech API clients append paths
like "/embeddings" and expect the base URL to already contain the "/v1" segment.
A host-only integration Base URL (e.g. "https://api.openai.com") therefore 404s
(older builds had the client add "/v1" itself, so this is also a backward-compat
regression). Normalize the configured base URL so it always ends in "/v1"
(idempotent): host-only, ".../v1", ".../v1/", and blank all resolve correctly.

Scoped to the OpenAI provider (OpenAI.java) only — the shared API classes are not
changed, so Azure OpenAI (base ".../openai/v1", azureAuth) and Perplexity/Grok
(OpenAIChatCompletionsApi with their own paths) are unaffected. The video API
still receives the /v1-stripped base as before.
2026-07-06 20:08:22 +05:30
Ling-Sen Peng 00ab8b341e fix(ai): attach user media to Anthropic requests
AnthropicChatModel.convertMessage() built the USER message from
userMsg.getText() only and silently dropped userMsg.getMedia(), so
images passed via the media input path never reached Claude — the model
received a text-only message and hallucinated. The OpenAI provider
already forwards media; Anthropic did not.

Convert user media into Anthropic image content blocks (base64 source)
alongside the text, mirroring OpenAIResponsesChatModel. Adds a
ContentBlock.image(mediaType, base64Data) factory (the image block type
and Source were already modeled, just unused).

Adds AnthropicChatModelMediaTest, which captures the outgoing
MessagesRequest and asserts the user message carries an image content
block with the verbatim base64 payload. Verified it fails before the fix
(media dropped -> bare string content) and passes after.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 12:31:18 -07:00
Viren Baraiya 4c24fdb879 remove(server-lite): delete server-lite module and all references (#1211)
* remove(server-lite): delete server-lite module and all references

Removes the server-lite Gradle subproject, its source code, build
scripts, and all CI/CD references. The server module with ui-next
is the canonical distribution going forward.

* fix(test): de-flake NestedForkJoinSubWorkflowSpec retry assertions

After retry(), the parent workflow is pushed onto the decider queue and
the background sweeper (sweeperThreadCount=1) can re-open CANCELED JOIN
tasks to IN_PROGRESS before the post-retry snapshot is read. Relax the
two pre-sweep JOIN assertions to accept either CANCELED (not yet swept)
or IN_PROGRESS (already re-opened), matching the fix applied to
HierarchicalForkJoinSubworkflowRetrySpec in #1148 and RerunSpec in #1206.

* Revert "fix(test): de-flake NestedForkJoinSubWorkflowSpec retry assertions"

This reverts commit 9a2bfad711c267cc845ac7e940605d119b5af638.
2026-06-30 00:10:57 -07:00
Viren Baraiya cde7b8e934 feat: Durable A2A (Agent2Agent) protocol — client, server, observability (#1195)
* feat(a2a): durable A2A protocol — client, server, observability

Add Agent2Agent (A2A) protocol support to the AI module, in both directions.

Client: CALL_AGENT / GET_AGENT_CARD / CANCEL_AGENT_TASK system tasks call remote
A2A agents over JSON-RPC with poll, streaming (SSE), and push modes. Durable by
design — deterministic messageId, state in the execution (not a thread), liveness
guards, and a push backstop. SSRF-guarded (IPv6 ULA + cloud-metadata; redirects off).

Server: expose any workflow as an A2A agent (one agent per workflow), idempotent
message/send -> startWorkflow (RETURN_EXISTING), tasks/get / tasks/cancel, and
multi-turn resume (a follow-up message/send completes the paused HUMAN/WAIT task
instead of starting a duplicate).

Observability: Micrometer counters via the shared Monitors registry and MDC
correlation keys across the A2A code paths.

Gated by conductor.integrations.ai.enabled (client) and conductor.a2a.server.enabled
(server).

* test(a2a): unit, real-agent interop, and real-engine durability tests

- Unit/wire tests (MockWebServer), embedded-agent e2e, push callback, server
  JSON-RPC dispatch + multi-turn resume, mapper/worker, and observability.
- A2ASdkInteropTest: drives the client against the official a2a-sdk reference
  agent launched as a subprocess (discovery, send, poll, streaming, message-mode);
  self-skips when no Python with a2a-sdk is available.
- A2ADurableEngineEndToEndTest (test-harness): CALL_AGENT through the real decider
  + AsyncSystemTaskExecutor + Redis, proving crash/restart resume from persistence.

* docs(a2a): integration guide, examples, UI task types, design notes

- docs/devguide/ai/a2a-integration.md (AI Cookbook nav) with worked examples:
  call/expose, multi-turn resume request/response, and push end-to-end.
- ai/examples: call / get-card / server + streaming / push / multi-turn / cancel,
  indexed in the examples README; runnable interop + durable demos under test
  resources.
- ui-next: register CALL_AGENT / GET_AGENT_CARD / CANCEL_AGENT_TASK task types.
- design/a2a: protocol study, durability proposal, and server design notes.

Note: a CI step to install a2a-sdk (so A2ASdkInteropTest runs in the build job)
is left as a follow-up — it needs a token with the `workflow` scope to land.

* refactor(a2a): move inbound server auth to enterprise (OSS open by default)

The A2A server's optional shared-secret api-key (conductor.a2a.server.api-key)
is removed from OSS: the server is now open by default, matching OSS Conductor
REST. Inbound authentication (API keys, OAuth/OIDC, mTLS, per-skill scopes,
signed Agent Cards) belongs to the enterprise build; front OSS with a
gateway/firewall.

Unchanged and kept in OSS as safe-by-default guards: client SSRF protection,
push-callback token auth, cross-agent execution isolation, and client→remote
per-call auth headers.

Removes authorized()/apiKey + the two api-key tests; docs and design notes
updated.

* docs(a2a): error-handling/troubleshooting, multi-agent + client multi-turn + LLM-pick examples

Docs: add an error-handling & retries section (FAILED vs FAILED_WITH_TERMINAL_ERROR
mapping for HTTP/JSON-RPC/SSRF/liveness) + a troubleshooting table; flesh out the
client multi-turn section with a worked SWITCH-on-input-required snippet; add an
"orchestrating multiple agents" subsection.

Examples (validated by ExampleWorkflowValidationTest):
- 27-a2a-multi-agent: FORK_JOIN calling agents in parallel → JOIN.
- 28-a2a-llm-pick-skill: GET_AGENT_CARD → LLM_CHAT_COMPLETE → CALL_AGENT.
- 29-a2a-client-multi-turn: SWITCH on input-required, re-call with same context/taskId.

* refactor(a2a)!: rename CALL_AGENT→AGENT, CANCEL_AGENT_TASK→CANCEL_AGENT + add agentType

Generalize the agent task types ahead of multi-runtime support:
- CALL_AGENT → AGENT (class CallAgentTask → AgentTask), CANCEL_AGENT_TASK →
  CANCEL_AGENT. GET_AGENT_CARD kept (discovery/"Agent Card" is A2A-specific).
- New input field `agentType` (default "a2a") on all three tasks — the extension
  point for native runtimes (langgraph, openai, …). Unknown values are rejected
  with a clear error; only "a2a" is implemented today.

Updated across enum, handlers, request models, mapper, callback, tests,
test-harness, examples, docs, design notes, and the ui-next task registration.

BREAKING CHANGE: workflows using type "CALL_AGENT"/"CANCEL_AGENT_TASK" must switch
to "AGENT"/"CANCEL_AGENT".

* docs(a2a): clean up post-rename residue (grammar, example names, phrasing)

Fix 'A AGENT'→'An AGENT' grammar (A2AMetrics, design), reword example descriptions and a doc sentence left awkward by the mechanical rename, and update legacy example workflow names (a2a_call_agent_* → a2a_agent_*).

* feat(a2a): server-side message/stream (SSE)

Exposed workflow-agents now support A2A message/stream. The endpoint returns an
SSE stream driven off a dedicated daemon pool: the initial Task, status-update
events as the workflow's A2A state changes, artifact-update events as output is
produced, and a final status-update at a terminal/input-required state (or when
the stream window elapses). The agent advertises capabilities.streaming=true.

A web-agnostic A2AStreamSink keeps SseEmitter out of A2AWorkflowAgent so the
stream driver stays unit-testable. Config: conductor.a2a.server.stream-poll-
interval-millis (500), stream-max-duration-seconds (300).

Tests: a mocked unit test asserting the task → artifact-update → final
status-update sequence, and a real-HTTP loopback (our client message/stream
against the server's SSE) aggregating to a completed task with artifacts.

Docs + design notes updated (streaming moves from follow-up to shipped).

* Update A2ADurableEngineEndToEndTest.java

* Update A2ADurableEngineEndToEndTest.java

* refactor(a2a): simplify client/server task code (no behavior change)

- AgentTask: replace hand-rolled isBlank/firstNonBlank/stripTrailingSlash/
  asInt/asLong helpers with commons-lang3 StringUtils + Number-only reads;
  reuse parseRequest().getPollIntervalSeconds() for pollInterval; dedup
  stateOf via A2AResults; fix the stale {@link #isAsyncComplete} javadoc to
  describe the real backstop-poll mechanism
- A2ACallbackResource: drop the deprecated query-param token path (untested,
  no legacy clients on a feature branch); StringUtils for isBlank
- A2AServerResource/A2AWorkflowAgent: delete the duplicate basePath()/URL
  building (delegate to the agent's agentUrl/new agentCardUrl); drop the
  no-op `id == null ? null : id` ternaries
- remove dead fields A2AMessage.referenceTaskIds and PushNotificationConfig.id

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

* docs(a2a): add mermaid use-case diagrams (client & server directions)

Diagrams rendering both A2A directions — Conductor as client (workflow calls
remote agents via the AGENT task) and as server (a workflow exposed as an A2A
agent):

- docs/devguide/ai/a2a-integration.md: both-directions overview flowchart,
  AGENT lifecycle sequence (poll/push/stream), FORK_JOIN→JOIN multi-agent
  fan-out, and the server message/send→workflow→tasks/get sequence
- design/a2a/08: overview flowchart + a sequence per direction (A/B)
- design/a2a/10: server sequence emphasizing the streaming and multi-turn
  resume paths

Rendered via pymdownx.superfences (mkdocs) and natively on GitHub.

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

* refactor(ai)!: rename package ai.models -> ai.model (singular)

Align the AI module with conductor-oss package conventions: every model
package in the codebase (com.netflix.conductor.common.model, core.model,
grpc/proto/model, ai.a2a.model) is singular `model` — `ai.models` was the
only plural `models` package in the entire repo, and sat one letter away
from the singular ai.a2a.model, which read as an inconsistency.

Pure mechanical rename: git mv of the package dir (history preserved) plus
package-decl/import/FQN updates across the module (main + test) and one doc
reference in ai/CONTRIBUTING.md. No type or behavior changes.

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

* fix /a2a endpoint not being reachable

* style: spotlessApply

* fix and upgrade version

* refactor for agentspan

* feat: add SQLite (sqlite-vec) vector database backend

Add an embedded, zero-infrastructure VectorDB backend implemented with the
sqlite-vec extension, alongside the existing pgvector/MongoDB/Pinecone backends.

- SqliteVectorDB stores embeddings in a vec0 virtual table and runs KNN via the
  sqlite-vec MATCH operator (l2/cosine/l1).
- The native vec0 binaries (linux/macos/windows) are downloaded + checksum-verified
  at build time (downloadSqliteVec) and bundled as jar resources; SqliteVecExtensions
  extracts the right one at runtime.
- SqliteVectorDBAutoConfiguration registers a "default" instance automatically when
  conductor.db.type=sqlite and conductor.integrations.ai.enabled=true, so the
  all-in-one SQLite server gets a working vector store with no external dependency.
- Adds unit tests, a real end-to-end round-trip test, a RAG example
  (30-rag-sqlite-vec.json), and documentation.

Note: a dedicated Linux e2e CI job (.github/workflows/ci.yml) is intentionally not
included here because the push token lacks the GitHub `workflow` scope; it must be
committed separately with a workflow-scoped token.

* test: enable workflow execution lock in integration profile to fix flaky specs

The integration-test profile disabled conductor.app.workflow-execution-lock-enabled
(since 2021), so acquireLock() was a no-op and the background WorkflowSweeper could
decide a workflow concurrently with specs' manual sweep()/asyncSystemTaskExecutor
calls. Two simultaneous decides could schedule duplicate tasks, intermittently
failing exact task-count assertions (FailureWorkflowSpec, and
HierarchicalForkJoinSubworkflowRetrySpec).

Enable the lock (matching production defaults; local_only lock is already configured)
so decides are serialized. Full :conductor-test-harness:test passes (252 tests).

* fix(build): wire downloadSqliteVec as a resource source so all consumers depend on it

The generated sqlite-vec resource directory is consumed by sourcesJar (and other
resource consumers), but only processResources depended on downloadSqliteVec. Gradle's
task-dependency validation (run as part of `build`) failed sourcesJar with an implicit
dependency error. Register the task provider itself as the resource srcDir so every
consumer depends on it automatically.

* fix(ai): harden sqlite-vec backend after review

Follow-up fixes from a deep review of the SQLite/sqlite-vec backend:

- SqliteVectorDB.upsertEmbeddings: restore autoCommit(true) in a finally block
  so connections are never returned to the Hikari pool in manual-commit mode.
- SqliteVectorDB.getSqliteClient: validate the extension path against an
  allowlist before interpolating it into load_extension(...).
- SqliteVectorDBAutoConfiguration.resolveDbPath: strip JDBC URL query params
  (e.g. ?busy_timeout=15000&journal_mode=WAL) so the derived *_vectordb.db path
  is a valid filename.
- SqliteVecExtensions: re-extract the bundled binary if the cached temp file was
  deleted between calls (stale-cache guard).
- VectorDBProvider: warn when an auto-registered default is skipped because an
  explicit instance with the same name already exists.

Adds SqliteVectorDBAutoConfigurationTest (resolveDbPath cases incl. query
params), SqliteVecExtensionsTest (platform detection), and a rollback/
autocommit-restore test in SqliteVectorDBTest.

* feat(ui-next): config forms for all AI / agentic task types

Wire up the workflow-editor task form dispatcher, Add-Task menu, icons, task
generators and JSON schemas for every AI task type exposed by the `ai/` module,
so each gets a real config form instead of the generic JSON editor.

- New bespoke forms: AGENT, GET_AGENT_CARD, CANCEL_AGENT, LIST_MCP_TOOLS,
  CALL_MCP_TOOL, GENERATE_IMAGE, GENERATE_AUDIO, GENERATE_VIDEO, GENERATE_PDF,
  LLM_SEARCH_EMBEDDINGS.
- Wire the existing-but-unwired LLM forms (chat/text complete, embeddings,
  index text/document, search index, get document) into TaskFormContent.
- LLM_CHAT_COMPLETE: OSS plain "Instructions" textarea (enterprise prompt-name
  picker remains pluggable via the plugin registry).
- A2A forms: agentType as a fixed A2A/Conductor radio; multi-row headers via the
  shared HTTP headers editor; pushNotification is a boolean toggle.
- CALL_MCP_TOOL arguments accept inline JSON or a ${variable} reference.
- Media-gen + search-embeddings reuse the field-driven LLMFormFields for
  provider/model/vectorDB/embedding selection (server-backed dropdowns).
- Full field coverage vs the backend input models; numeric fields use coerceTo
  so the workflow JSON stores real numbers while still accepting ${variables}.
- A2A tasks added to the Agentic Orchestration quick-add section.

* fix(ui-next): address PR review on AI task forms

- Schemas.ts: add the new AI/agentic task types to genericSchema's type enum so
  the code (JSON) editor no longer warns "type is not one of ..." for AGENT,
  GENERATE_*, *_MCP_*, LLM_SEARCH_EMBEDDINGS, CHUNK_TEXT, LIST_FILES, PARSE_DOCUMENT.
- AgentTaskForm: fix spacing so the "Push backstop poll" field's floating label
  no longer collides with the push-notification toggle (group switches, separate
  the input row).
- LLMSearchEmbeddingsTaskForm: add spacing so the helper text no longer collides
  with the Query field's floating label.
- QuickAddMenu: fill the two empty Agentic Orchestration slots (Call MCP Tool,
  Generate Image) for a clean two-row grid.

* fix(ui-next): snug node height for AI/agentic task cards

AI task cards render header-only content (no custom body), but fell through to
the DEFAULT node height (100px), leaving empty space below the label on the
canvas. Size the AI/agentic header-only task family to 80px (matching the
header-only FORK_JOIN node) so the cards fit their content.

---------

Co-authored-by: Nicholas Cole <68611647+NicholasDCole@users.noreply.github.com>
Co-authored-by: nicholascole <nicholas.colesd@gmail.com>
2026-06-29 23:41:58 -07:00
Dale Brady bdf268eeec feat: Embed AgentSpan agents into OSS Conductor (runtime + UI), gated by conductor.integrations.ai.enabled (#1213) 2026-06-25 22:44:10 -07:00