Skip to content

Commit fb4cbff

Browse files
fix(map): use Agent API endpoint for generatePlan (#4)
- Changed from /api/v1/orchestrator/plan to /api/request with request_type: 'multi-agent-plan' to match other SDKs - Added parsePlanResponse() to handle Agent API response format (nested data.steps, data.domain, etc.) - Updated test to use new endpoint and response format - Fixed null-safety issues with Map.of() by using HashMap
1 parent 8a17c37 commit fb4cbff

3 files changed

Lines changed: 132 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Changelog
2+
3+
All notable changes to the AxonFlow Java SDK will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Fixed
11+
12+
- **MAP Endpoint** - Fixed `generatePlan()` to use correct Agent API endpoint
13+
- Changed from `/api/v1/orchestrator/plan` to `/api/request` with `request_type: "multi-agent-plan"`
14+
- Added proper response parsing for Agent API format
15+
- Fixed null-safety issues with request context
16+
17+
## [1.1.0] - 2025-12-19
18+
19+
### Added
20+
21+
- **LLM Interceptors** - Transparent governance for LLM API calls (#1)
22+
- `OpenAIInterceptor` for OpenAI API interception
23+
- `AnthropicInterceptor` for Anthropic API interception
24+
- `GeminiInterceptor` for Google Generative AI interception
25+
- Policy enforcement and audit logging for all providers
26+
- Full feature parity with other SDKs for LLM interceptors
27+
- **Self-Hosted Zero-Config Tests** - Auth header verification for localhost (#2)
28+
- Tests verify auth headers are skipped for localhost endpoints
29+
30+
## [1.0.0] - 2025-12-04
31+
32+
### Added
33+
34+
- Initial release of AxonFlow Java SDK
35+
- Core client with `executeQuery()` for governed AI calls
36+
- Policy enforcement with `PolicyViolationException`
37+
- **Gateway Mode** support
38+
- `getPolicyApprovedContext()` for pre-checks
39+
- `auditLLMCall()` for compliance logging
40+
- **Multi-Agent Planning**
41+
- `generatePlan()` for creating execution plans
42+
- `executePlan()` for running plans
43+
- `getPlanStatus()` for checking plan status
44+
- **MCP Connectors**
45+
- `listConnectors()` for available connectors
46+
- `installConnector()` for connector installation
47+
- `queryConnector()` for connector queries
48+
- Comprehensive type definitions with Jackson
49+
- Retry logic with exponential backoff (OkHttp)
50+
- Response caching with Caffeine
51+
- Self-hosted mode for localhost deployments
52+
- Java 11+ compatibility
53+
- Maven Central publishing support

src/main/java/com/getaxonflow/sdk/AxonFlow.java

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import java.io.Closeable;
3232
import java.io.IOException;
33+
import java.util.Collections;
3334
import java.util.List;
3435
import java.util.Map;
3536
import java.util.Objects;
@@ -361,6 +362,9 @@ public CompletableFuture<ClientResponse> executeQueryAsync(ClientRequest request
361362
/**
362363
* Generates a multi-agent plan for a complex task.
363364
*
365+
* <p>This method uses the Agent API with request_type "multi-agent-plan"
366+
* to generate and execute plans through the governance layer.
367+
*
364368
* @param request the plan request
365369
* @return the generated plan
366370
* @throws PlanExecutionException if plan generation fails
@@ -369,13 +373,83 @@ public PlanResponse generatePlan(PlanRequest request) {
369373
Objects.requireNonNull(request, "request cannot be null");
370374

371375
return retryExecutor.execute(() -> {
372-
Request httpRequest = buildRequest("POST", "/api/v1/orchestrator/plan", request);
376+
// Build agent request format - use HashMap to allow null-safe values
377+
String userToken = request.getUserToken();
378+
if (userToken == null) {
379+
userToken = config.getClientId() != null ? config.getClientId() : "default";
380+
}
381+
String clientId = config.getClientId() != null ? config.getClientId() : "default";
382+
String domain = request.getDomain() != null ? request.getDomain() : "generic";
383+
384+
Map<String, Object> agentRequest = new java.util.HashMap<>();
385+
agentRequest.put("query", request.getObjective());
386+
agentRequest.put("user_token", userToken);
387+
agentRequest.put("client_id", clientId);
388+
agentRequest.put("request_type", "multi-agent-plan");
389+
agentRequest.put("context", Map.of("domain", domain));
390+
391+
Request httpRequest = buildRequest("POST", "/api/request", agentRequest);
373392
try (Response response = httpClient.newCall(httpRequest).execute()) {
374-
return parseResponse(response, PlanResponse.class);
393+
return parsePlanResponse(response, request.getDomain());
375394
}
376395
}, "generatePlan");
377396
}
378397

398+
/**
399+
* Parses the Agent API response format into PlanResponse.
400+
* The Agent API returns: {success, plan_id, data: {steps, domain, ...}, metadata, result}
401+
*/
402+
@SuppressWarnings("unchecked")
403+
private PlanResponse parsePlanResponse(Response response, String requestDomain) throws IOException {
404+
handleErrorResponse(response);
405+
406+
ResponseBody body = response.body();
407+
if (body == null) {
408+
throw new AxonFlowException("Empty response body", response.code(), null);
409+
}
410+
411+
String json = body.string();
412+
Map<String, Object> agentResponse = objectMapper.readValue(json,
413+
new TypeReference<Map<String, Object>>() {});
414+
415+
// Check for errors
416+
Boolean success = (Boolean) agentResponse.get("success");
417+
if (success == null || !success) {
418+
String error = (String) agentResponse.get("error");
419+
throw new PlanExecutionException(error != null ? error : "Plan generation failed");
420+
}
421+
422+
// Extract fields from Agent API response format
423+
String planId = (String) agentResponse.get("plan_id");
424+
Map<String, Object> data = (Map<String, Object>) agentResponse.get("data");
425+
Map<String, Object> metadata = (Map<String, Object>) agentResponse.get("metadata");
426+
String result = (String) agentResponse.get("result");
427+
428+
// Extract nested fields from data
429+
List<PlanStep> steps = Collections.emptyList();
430+
String domain = requestDomain != null ? requestDomain : "generic";
431+
Integer complexity = null;
432+
Boolean parallel = null;
433+
String estimatedDuration = null;
434+
435+
if (data != null) {
436+
// Parse steps if present
437+
List<Map<String, Object>> rawSteps = (List<Map<String, Object>>) data.get("steps");
438+
if (rawSteps != null) {
439+
steps = rawSteps.stream()
440+
.map(stepMap -> objectMapper.convertValue(stepMap, PlanStep.class))
441+
.toList();
442+
}
443+
domain = data.get("domain") != null ? (String) data.get("domain") : domain;
444+
complexity = data.get("complexity") != null ? ((Number) data.get("complexity")).intValue() : null;
445+
parallel = (Boolean) data.get("parallel");
446+
estimatedDuration = (String) data.get("estimated_duration");
447+
}
448+
449+
return new PlanResponse(planId, steps, domain, complexity, parallel,
450+
estimatedDuration, metadata, null, result);
451+
}
452+
379453
/**
380454
* Asynchronously generates a multi-agent plan.
381455
*

src/test/java/com/getaxonflow/sdk/AxonFlowTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,12 @@ void generatePlanShouldRequireRequest() {
229229
@Test
230230
@DisplayName("generatePlanAsync should return future")
231231
void generatePlanAsyncShouldReturnFuture() throws Exception {
232-
stubFor(post(urlEqualTo("/api/v1/orchestrator/plan"))
232+
// Now uses Agent API endpoint with request_type: multi-agent-plan
233+
stubFor(post(urlEqualTo("/api/request"))
233234
.willReturn(aResponse()
234235
.withStatus(200)
235236
.withHeader("Content-Type", "application/json")
236-
.withBody("{\"plan_id\":\"plan_123\",\"steps\":[]}")));
237+
.withBody("{\"success\":true,\"plan_id\":\"plan_123\",\"data\":{\"steps\":[]}}")));
237238

238239
PlanRequest request = PlanRequest.builder()
239240
.objective("test")

0 commit comments

Comments
 (0)