agent-api-server 2.1.10__tar.gz → 2.1.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/PKG-INFO +1 -1
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/agent.go +19 -16
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/agenthub-acp +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/main.go +40 -107
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/thread.py +0 -150
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/pyproject.toml +1 -1
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/README.md +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/.gitignore +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/agent_api_server.iml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/misc.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/modules.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/vcs.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/workspace.xml +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/agent_card_builder.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/call_a2a_client.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/langgraph_executor.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/redis_task_store.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/server.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/Makefile +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/go.mod +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/go.sum +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/agent_registration.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/api.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/config.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/graph.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/api/v1/schema.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/app.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/cache/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/cache/redis_cache.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/callback_handler.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/client/css/styles.css +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/client/favicon.ico +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/client/index.html +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/client/js/app.js +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/client/js/index.umd.js +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/config_center/config_center.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/configs/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/configs/config.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/dynamic_llm/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/dynamic_llm/dynamic_llm.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/base.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/nats_listener.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/redis_listener.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/log/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/log/formatters.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/log/logging.json +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/mcp_convert/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/mcp_convert/mcp_convert.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/mcp_server.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/memory/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/memory/postgres.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/agent_registry.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/modes/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/modes/chain.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/modes/discussion.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/orchestrator.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/types.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/register/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/register/register.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/service.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/service_hub/service_hub.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/service_hub/service_hub_test.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/services/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/services/config_builder.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/services/stream_service.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/ase.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/base_model.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/common.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/decode_token.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/detect_message.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/exceptions.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/get_model_info.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/graph_loader.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/message.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/model_config.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/util_func.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/__init__.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/client.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/credential.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/encoding.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sso_service.py +0 -0
- {agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/startup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: agent-api-server
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.12
|
|
4
4
|
Summary: A Langgraph agent API server that implements Langgraph agent's web capabilities and can interact with chatbot
|
|
5
5
|
Author: zijie.zhang@advantech.com.cn
|
|
6
6
|
Requires-Python: >=3.10,<3.13
|
|
@@ -39,6 +39,12 @@ type SessionState struct {
|
|
|
39
39
|
UpdatedAt string `json:"updated_at,omitempty"`
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// pendingToolCall holds buffered tool call info until the tool result is available.
|
|
43
|
+
type pendingToolCall struct {
|
|
44
|
+
name string
|
|
45
|
+
args interface{}
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
// AgentHubACPAgent implements the ACP protocol over stdio, proxying to an HTTP backend.
|
|
43
49
|
type AgentHubACPAgent struct {
|
|
44
50
|
serviceURL string
|
|
@@ -54,7 +60,7 @@ type AgentHubACPAgent struct {
|
|
|
54
60
|
mu sync.Mutex
|
|
55
61
|
httpClient *http.Client
|
|
56
62
|
sessions map[string]*SessionState
|
|
57
|
-
pendingToolCalls map[string]map[string]
|
|
63
|
+
pendingToolCalls map[string]map[string]*pendingToolCall // sessionID -> tcID -> info
|
|
58
64
|
streamedNodes map[string]map[string]bool // sessionID -> set of nodes
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -77,7 +83,7 @@ func NewAgentHubACPAgent(
|
|
|
77
83
|
extraInputs: extraInputs,
|
|
78
84
|
streamMode: streamMode,
|
|
79
85
|
sessions: make(map[string]*SessionState),
|
|
80
|
-
pendingToolCalls: make(map[string]map[string]
|
|
86
|
+
pendingToolCalls: make(map[string]map[string]*pendingToolCall),
|
|
81
87
|
streamedNodes: make(map[string]map[string]bool),
|
|
82
88
|
httpClient: &http.Client{
|
|
83
89
|
Timeout: 300 * time.Second,
|
|
@@ -348,24 +354,22 @@ func (a *AgentHubACPAgent) processSSEEvent(sessionID, raw string) {
|
|
|
348
354
|
a.mu.Lock()
|
|
349
355
|
pending := a.pendingToolCalls[sessionID]
|
|
350
356
|
if pending == nil {
|
|
351
|
-
pending = make(map[string]
|
|
357
|
+
pending = make(map[string]*pendingToolCall)
|
|
352
358
|
a.pendingToolCalls[sessionID] = pending
|
|
353
359
|
}
|
|
354
360
|
a.mu.Unlock()
|
|
355
361
|
for _, tc := range toolCalls {
|
|
356
|
-
tcID
|
|
357
|
-
|
|
358
|
-
tcID = uuid.New().String()
|
|
359
|
-
}
|
|
362
|
+
// Always use UUID for tcID to avoid invalid IDs from LangChain
|
|
363
|
+
tcID := uuid.New().String()
|
|
360
364
|
tcName, _ := tc["name"].(string)
|
|
361
365
|
if tcName == "" {
|
|
362
366
|
tcName = "unknown"
|
|
363
367
|
}
|
|
364
368
|
tcArgs := tc["args"]
|
|
369
|
+
// Buffer tool call info; emit complete tool_call when tools_message arrives
|
|
365
370
|
a.mu.Lock()
|
|
366
|
-
pending[tcID] = tcName
|
|
371
|
+
pending[tcID] = &pendingToolCall{name: tcName, args: tcArgs}
|
|
367
372
|
a.mu.Unlock()
|
|
368
|
-
a.sendStartToolCall(sessionID, tcID, tcName, tcArgs, nil, "")
|
|
369
373
|
}
|
|
370
374
|
} else if text != "" {
|
|
371
375
|
a.mu.Lock()
|
|
@@ -390,12 +394,13 @@ func (a *AgentHubACPAgent) processSSEEvent(sessionID, raw string) {
|
|
|
390
394
|
a.mu.Unlock()
|
|
391
395
|
|
|
392
396
|
if len(pending) > 0 {
|
|
393
|
-
for tcID,
|
|
397
|
+
for tcID, info := range pending {
|
|
394
398
|
output := text
|
|
395
399
|
if output == "" {
|
|
396
|
-
output =
|
|
400
|
+
output = info.name + " completed"
|
|
397
401
|
}
|
|
398
|
-
|
|
402
|
+
// Emit single complete tool_call with rawInput + rawOutput
|
|
403
|
+
a.sendStartToolCall(sessionID, tcID, info.name, info.args, output, "completed")
|
|
399
404
|
}
|
|
400
405
|
a.mu.Lock()
|
|
401
406
|
delete(a.pendingToolCalls, sessionID)
|
|
@@ -412,10 +417,8 @@ func (a *AgentHubACPAgent) processSSEEvent(sessionID, raw string) {
|
|
|
412
417
|
}
|
|
413
418
|
}
|
|
414
419
|
for _, tc := range toolCalls {
|
|
415
|
-
tcID
|
|
416
|
-
|
|
417
|
-
tcID = uuid.New().String()
|
|
418
|
-
}
|
|
420
|
+
// Always use UUID for tcID to avoid invalid IDs from LangChain
|
|
421
|
+
tcID := uuid.New().String()
|
|
419
422
|
tcName, _ := tc["name"].(string)
|
|
420
423
|
if tcName == "" {
|
|
421
424
|
tcName = "unknown"
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/acp_bridge/go/agenthub-acp
RENAMED
|
Binary file
|
|
@@ -301,7 +301,6 @@ func (a *AgentHubACPAgent) handleLoadSession(req jsonrpcRequest) *jsonrpcRespons
|
|
|
301
301
|
func (a *AgentHubACPAgent) loadSessionHistory(sessionID, cwd string) {
|
|
302
302
|
params := url.Values{
|
|
303
303
|
"user_input_field": {a.userInputField},
|
|
304
|
-
"include_node_history": {"true"},
|
|
305
304
|
}
|
|
306
305
|
resp, err := a.doRequest("GET", "/api/v1/thread/"+sessionID+"/history", nil, params)
|
|
307
306
|
if err != nil {
|
|
@@ -317,129 +316,48 @@ func (a *AgentHubACPAgent) loadSessionHistory(sessionID, cwd string) {
|
|
|
317
316
|
|
|
318
317
|
var data struct {
|
|
319
318
|
Messages []map[string]interface{} `json:"messages"`
|
|
320
|
-
NodeExecutions []map[string]interface{} `json:"node_executions"`
|
|
321
319
|
}
|
|
322
320
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
|
323
321
|
log.Printf("Failed to decode session history: %v", err)
|
|
324
322
|
return
|
|
325
323
|
}
|
|
326
324
|
|
|
327
|
-
|
|
328
|
-
a.replayNodeExecutions(sessionID, data.NodeExecutions)
|
|
329
|
-
} else {
|
|
330
|
-
a.replayMessages(sessionID, data.Messages)
|
|
331
|
-
}
|
|
325
|
+
a.replayMessages(sessionID, data.Messages)
|
|
332
326
|
|
|
333
327
|
log.Printf("Loaded session %s with %d messages", sessionID, len(data.Messages))
|
|
334
328
|
}
|
|
335
329
|
|
|
336
|
-
// replayNodeExecutions replays session history using node_executions data.
|
|
337
|
-
// Each nodeExecution represents one turn (user question + AI response).
|
|
338
|
-
// We process messages in order within each turn to maintain proper conversation flow.
|
|
339
|
-
func (a *AgentHubACPAgent) replayNodeExecutions(sessionID string, nodeExecutions []map[string]interface{}) {
|
|
340
|
-
shownContents := make(map[string]bool)
|
|
341
|
-
|
|
342
|
-
for _, execInfo := range nodeExecutions {
|
|
343
|
-
nodesRaw, _ := execInfo["nodes"].([]interface{})
|
|
344
|
-
execMessagesRaw, _ := execInfo["messages"].([]interface{})
|
|
345
330
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
331
|
+
// replayMessages replays session history using simple messages list.
|
|
332
|
+
func (a *AgentHubACPAgent) replayMessages(sessionID string, messages []map[string]interface{}) {
|
|
333
|
+
// Pre-scan: collect tool results by name for including in tool_call events
|
|
334
|
+
toolResultsByName := make(map[string][]string)
|
|
335
|
+
for _, msg := range messages {
|
|
336
|
+
if msgType, _ := msg["type"].(string); msgType == "tool" {
|
|
337
|
+
toolName, _ := msg["name"].(string)
|
|
338
|
+
if toolName == "" {
|
|
339
|
+
toolName = "unknown"
|
|
350
340
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
var execMessages []map[string]interface{}
|
|
354
|
-
for _, m := range execMessagesRaw {
|
|
355
|
-
if mm, ok := m.(map[string]interface{}); ok {
|
|
356
|
-
execMessages = append(execMessages, mm)
|
|
341
|
+
if c, _ := msg["content"].(string); c != "" {
|
|
342
|
+
toolResultsByName[toolName] = append(toolResultsByName[toolName], c)
|
|
357
343
|
}
|
|
358
344
|
}
|
|
359
|
-
|
|
360
|
-
// Process messages in order: first emit human message, then AI/tool responses
|
|
361
|
-
humanEmitted := false
|
|
362
|
-
for _, msg := range execMessages {
|
|
363
|
-
msgType, _ := msg["type"].(string)
|
|
364
|
-
msgContent, _ := msg["content"].(string)
|
|
365
|
-
msgName, _ := msg["name"].(string)
|
|
366
|
-
|
|
367
|
-
if msgType == "human" {
|
|
368
|
-
// Emit user message
|
|
369
|
-
if msgContent != "" {
|
|
370
|
-
a.sendUserMessage(sessionID, msgContent)
|
|
371
|
-
humanEmitted = true
|
|
372
|
-
}
|
|
373
|
-
continue
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// For AI and tool messages, check if this is a node output
|
|
377
|
-
if nodeSet := make(map[string]bool); len(nodes) > 0 {
|
|
378
|
-
for _, n := range nodes {
|
|
379
|
-
nodeSet[n] = true
|
|
380
|
-
}
|
|
381
|
-
if nodeSet[msgName] && msgContent != "" {
|
|
382
|
-
// This is a node output - emit as tool call + agent message
|
|
383
|
-
if !shownContents[msgContent] {
|
|
384
|
-
a.sendStartToolCall(sessionID, uuid.New().String(), msgName, nil, nil, "completed")
|
|
385
|
-
a.sendAgentMessage(sessionID, fmt.Sprintf("[%s]\n%s", msgName, msgContent))
|
|
386
|
-
shownContents[msgContent] = true
|
|
387
|
-
}
|
|
388
|
-
continue
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Handle AI messages with tool_calls
|
|
393
|
-
if msgType == "ai" {
|
|
394
|
-
if toolCallsRaw, ok := msg["tool_calls"].([]interface{}); ok {
|
|
395
|
-
for _, tc := range toolCallsRaw {
|
|
396
|
-
if tcm, ok := tc.(map[string]interface{}); ok {
|
|
397
|
-
tcName, _ := tcm["name"].(string)
|
|
398
|
-
if tcName == "" {
|
|
399
|
-
tcName = "unknown"
|
|
400
|
-
}
|
|
401
|
-
tcArgs := tcm["args"]
|
|
402
|
-
a.sendStartToolCall(sessionID, uuid.New().String(), tcName, tcArgs, nil, "completed")
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if msgContent != "" && !shownContents[msgContent] {
|
|
407
|
-
a.sendAgentMessage(sessionID, msgContent)
|
|
408
|
-
shownContents[msgContent] = true
|
|
409
|
-
}
|
|
410
|
-
} else if msgType == "tool" {
|
|
411
|
-
toolName, _ := msg["name"].(string)
|
|
412
|
-
if toolName == "" {
|
|
413
|
-
toolName = "unknown"
|
|
414
|
-
}
|
|
415
|
-
if msgContent != "" && !shownContents[msgContent] {
|
|
416
|
-
a.sendAgentMessage(sessionID, fmt.Sprintf("[tool:%s] %s", toolName, msgContent))
|
|
417
|
-
shownContents[msgContent] = true
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// If no human message was emitted but we have messages, something is wrong
|
|
423
|
-
// (this shouldn't happen with proper turn detection)
|
|
424
|
-
if !humanEmitted && len(execMessages) > 0 {
|
|
425
|
-
log.Printf("Warning: turn with no human message emitted, nodes=%v", nodes)
|
|
426
|
-
}
|
|
427
345
|
}
|
|
428
|
-
|
|
346
|
+
toolResultIdx := make(map[string]int)
|
|
429
347
|
|
|
430
|
-
// replayMessages replays session history using simple messages list.
|
|
431
|
-
func (a *AgentHubACPAgent) replayMessages(sessionID string, messages []map[string]interface{}) {
|
|
432
348
|
for _, msg := range messages {
|
|
433
349
|
msgType, _ := msg["type"].(string)
|
|
434
350
|
msgContent, _ := msg["content"].(string)
|
|
351
|
+
msgName, _ := msg["name"].(string)
|
|
435
352
|
|
|
436
353
|
switch msgType {
|
|
437
354
|
case "human":
|
|
438
355
|
a.sendUserMessage(sessionID, msgContent)
|
|
439
356
|
|
|
440
357
|
case "ai":
|
|
441
|
-
//
|
|
442
|
-
if toolCallsRaw, ok := msg["tool_calls"].([]interface{}); ok {
|
|
358
|
+
// Check if this AI message has tool_calls (ReAct Agent pattern)
|
|
359
|
+
if toolCallsRaw, ok := msg["tool_calls"].([]interface{}); ok && len(toolCallsRaw) > 0 {
|
|
360
|
+
// ReAct Agent pattern: AI message with tool_calls
|
|
443
361
|
for _, tc := range toolCallsRaw {
|
|
444
362
|
if tcm, ok := tc.(map[string]interface{}); ok {
|
|
445
363
|
tcName, _ := tcm["name"].(string)
|
|
@@ -447,20 +365,35 @@ func (a *AgentHubACPAgent) replayMessages(sessionID string, messages []map[strin
|
|
|
447
365
|
tcName = "unknown"
|
|
448
366
|
}
|
|
449
367
|
tcArgs := tcm["args"]
|
|
450
|
-
|
|
368
|
+
tcID := uuid.New().String()
|
|
369
|
+
// Include tool output in the tool_call event
|
|
370
|
+
var rawOutput interface{}
|
|
371
|
+
if results, ok := toolResultsByName[tcName]; ok {
|
|
372
|
+
idx := toolResultIdx[tcName]
|
|
373
|
+
if idx < len(results) {
|
|
374
|
+
rawOutput = results[idx]
|
|
375
|
+
toolResultIdx[tcName] = idx + 1
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
a.sendStartToolCall(sessionID, tcID, tcName, tcArgs, rawOutput, "completed")
|
|
451
379
|
}
|
|
452
380
|
}
|
|
453
|
-
|
|
454
|
-
|
|
381
|
+
if msgContent != "" {
|
|
382
|
+
a.sendAgentMessage(sessionID, msgContent)
|
|
383
|
+
}
|
|
384
|
+
} else if msgName != "" && msgContent != "" {
|
|
385
|
+
// Node pattern: AI message without tool_calls, but has name (node name)
|
|
386
|
+
tcID := uuid.New().String()
|
|
387
|
+
a.sendStartToolCall(sessionID, tcID, msgName, nil, msgContent, "completed")
|
|
388
|
+
a.sendAgentMessage(sessionID, fmt.Sprintf("[%s]\n%s", msgName, msgContent))
|
|
389
|
+
} else if msgContent != "" {
|
|
390
|
+
// Regular AI message
|
|
455
391
|
a.sendAgentMessage(sessionID, msgContent)
|
|
456
392
|
}
|
|
457
393
|
|
|
458
394
|
case "tool":
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
toolName = "unknown"
|
|
462
|
-
}
|
|
463
|
-
a.sendAgentMessage(sessionID, fmt.Sprintf("[tool:%s] %s", toolName, msgContent))
|
|
395
|
+
// Tool results are already included in the preceding AI message's
|
|
396
|
+
// tool_call rawOutput, so skip standalone tool messages.
|
|
464
397
|
}
|
|
465
398
|
}
|
|
466
399
|
}
|
|
@@ -626,7 +626,6 @@ async def get_thread_history(
|
|
|
626
626
|
thread_id: str,
|
|
627
627
|
limit: int = Query(50, ge=1, le=200, description="Max messages to return"),
|
|
628
628
|
user_input_field: Optional[str] = Query(None, description="User input field name"),
|
|
629
|
-
include_node_history: bool = Query(False, description="Include node execution history"),
|
|
630
629
|
) -> Dict[str, Any]:
|
|
631
630
|
try:
|
|
632
631
|
from agent_api_server.memory.postgres import AsyncPostgresCheckpointer
|
|
@@ -706,155 +705,6 @@ async def get_thread_history(
|
|
|
706
705
|
"next_config": state.next if hasattr(state, "next") else None,
|
|
707
706
|
}
|
|
708
707
|
|
|
709
|
-
if include_node_history:
|
|
710
|
-
node_executions = []
|
|
711
|
-
snapshots = []
|
|
712
|
-
async for snapshot in graph_instance.aget_state_history(config):
|
|
713
|
-
snapshots.append(snapshot)
|
|
714
|
-
|
|
715
|
-
snapshots.reverse()
|
|
716
|
-
|
|
717
|
-
from langchain_core.messages import HumanMessage as _HumanMessage
|
|
718
|
-
|
|
719
|
-
# Detect turns based on state history snapshots.
|
|
720
|
-
# Each snapshot represents a checkpoint; we detect new user
|
|
721
|
-
# inputs by comparing either string values or HumanMessage
|
|
722
|
-
# counts (when input_field points to a list like "messages").
|
|
723
|
-
turn_boundaries = [] # list of (msg_index_of_human_msg, user_text)
|
|
724
|
-
|
|
725
|
-
prev_human_count = 0
|
|
726
|
-
prev_user_input_str = None
|
|
727
|
-
|
|
728
|
-
for i, snapshot in enumerate(snapshots):
|
|
729
|
-
msgs = snapshot.values.get("messages", []) if snapshot.values else []
|
|
730
|
-
if not (snapshot.values and input_field):
|
|
731
|
-
continue
|
|
732
|
-
|
|
733
|
-
val = snapshot.values.get(input_field)
|
|
734
|
-
current_user_input = None
|
|
735
|
-
|
|
736
|
-
if val and isinstance(val, str):
|
|
737
|
-
current_user_input = val.strip()
|
|
738
|
-
if (current_user_input is not None and
|
|
739
|
-
current_user_input != prev_user_input_str):
|
|
740
|
-
# Find the last HumanMessage in current msgs
|
|
741
|
-
human_idx = None
|
|
742
|
-
for j in range(len(msgs) - 1, -1, -1):
|
|
743
|
-
if isinstance(msgs[j], _HumanMessage):
|
|
744
|
-
human_idx = j
|
|
745
|
-
break
|
|
746
|
-
if human_idx is not None:
|
|
747
|
-
turn_boundaries.append((human_idx, current_user_input))
|
|
748
|
-
prev_user_input_str = current_user_input
|
|
749
|
-
|
|
750
|
-
elif isinstance(val, list):
|
|
751
|
-
# input_field points to the messages list itself.
|
|
752
|
-
# Find new HumanMessages by comparing count with previous snapshot.
|
|
753
|
-
def _is_human_message(m):
|
|
754
|
-
"""Check if a message is a HumanMessage (supports both object and dict formats)."""
|
|
755
|
-
if isinstance(m, _HumanMessage):
|
|
756
|
-
return True
|
|
757
|
-
if isinstance(m, dict) and m.get("type") == "human":
|
|
758
|
-
return True
|
|
759
|
-
if hasattr(m, "type") and getattr(m, "type", None) == "human":
|
|
760
|
-
return True
|
|
761
|
-
return False
|
|
762
|
-
|
|
763
|
-
def _get_message_content(m):
|
|
764
|
-
"""Get content from a message (supports both object and dict formats)."""
|
|
765
|
-
if isinstance(m, dict):
|
|
766
|
-
return m.get("content", "")
|
|
767
|
-
return m.content if hasattr(m, "content") else str(m)
|
|
768
|
-
|
|
769
|
-
human_indices_in_msgs = [
|
|
770
|
-
j for j, m in enumerate(val)
|
|
771
|
-
if _is_human_message(m)
|
|
772
|
-
]
|
|
773
|
-
human_count = len(human_indices_in_msgs)
|
|
774
|
-
if human_count > prev_human_count:
|
|
775
|
-
# New HumanMessage(s) detected.
|
|
776
|
-
# Use the ACTUAL index in the messages list,
|
|
777
|
-
# not human_count - 1 (which is wrong when AI/tool
|
|
778
|
-
# messages are interspersed).
|
|
779
|
-
for k in range(prev_human_count, human_count):
|
|
780
|
-
actual_idx = human_indices_in_msgs[k]
|
|
781
|
-
new_human = val[actual_idx]
|
|
782
|
-
content_str = _get_message_content(new_human)
|
|
783
|
-
if isinstance(content_str, str):
|
|
784
|
-
content_str = content_str.strip()
|
|
785
|
-
else:
|
|
786
|
-
content_str = str(content_str)
|
|
787
|
-
turn_boundaries.append((actual_idx, content_str))
|
|
788
|
-
prev_human_count = human_count
|
|
789
|
-
|
|
790
|
-
# Deduplicate boundaries by msg index (keep first occurrence)
|
|
791
|
-
seen_indices = set()
|
|
792
|
-
unique_boundaries = []
|
|
793
|
-
for idx, text in turn_boundaries:
|
|
794
|
-
if idx not in seen_indices:
|
|
795
|
-
seen_indices.add(idx)
|
|
796
|
-
unique_boundaries.append((idx, text))
|
|
797
|
-
turn_boundaries = unique_boundaries
|
|
798
|
-
|
|
799
|
-
# Build node_executions from turn boundaries
|
|
800
|
-
final_snapshot = snapshots[-1] if snapshots else None
|
|
801
|
-
if final_snapshot and final_snapshot.values:
|
|
802
|
-
final_msgs = final_snapshot.values.get("messages", [])
|
|
803
|
-
|
|
804
|
-
# Collect all node names from all snapshots for each turn range
|
|
805
|
-
for turn_idx, (human_msg_idx, user_text) in enumerate(turn_boundaries):
|
|
806
|
-
# Determine message range for this turn
|
|
807
|
-
start_idx = human_msg_idx
|
|
808
|
-
if turn_idx + 1 < len(turn_boundaries):
|
|
809
|
-
end_idx = turn_boundaries[turn_idx + 1][0]
|
|
810
|
-
else:
|
|
811
|
-
end_idx = len(final_msgs)
|
|
812
|
-
|
|
813
|
-
# Collect node names that executed in this turn's snapshots
|
|
814
|
-
turn_nodes = []
|
|
815
|
-
for snapshot in snapshots:
|
|
816
|
-
s_msgs = snapshot.values.get("messages", []) if snapshot.values else []
|
|
817
|
-
s_len = len(s_msgs)
|
|
818
|
-
# This snapshot belongs to this turn if it overlaps
|
|
819
|
-
# with the turn's message range
|
|
820
|
-
if s_len > start_idx and (turn_idx + 1 >= len(turn_boundaries) or s_len <= end_idx + 1):
|
|
821
|
-
node_names = [task.name for task in snapshot.tasks if task.name != "__start__"] if snapshot.tasks else []
|
|
822
|
-
for n in node_names:
|
|
823
|
-
if n not in turn_nodes:
|
|
824
|
-
turn_nodes.append(n)
|
|
825
|
-
|
|
826
|
-
# Build turn messages
|
|
827
|
-
turn_messages = [{
|
|
828
|
-
"type": "human",
|
|
829
|
-
"content": user_text,
|
|
830
|
-
"name": None,
|
|
831
|
-
"tool_calls": [],
|
|
832
|
-
}]
|
|
833
|
-
|
|
834
|
-
# Add non-human messages in range (skip the human message at start_idx
|
|
835
|
-
# since we already added it above)
|
|
836
|
-
for msg in final_msgs[start_idx + 1:end_idx]:
|
|
837
|
-
turn_messages.append({
|
|
838
|
-
"type": getattr(msg, "type", "unknown"),
|
|
839
|
-
"content": msg.content if hasattr(msg, "content") else str(msg),
|
|
840
|
-
"name": getattr(msg, "name", None),
|
|
841
|
-
"tool_calls": [
|
|
842
|
-
{"name": tc.get("name", ""), "args": tc.get("args", {})}
|
|
843
|
-
for tc in (getattr(msg, "tool_calls", []) or [])
|
|
844
|
-
] if hasattr(msg, "tool_calls") else [],
|
|
845
|
-
})
|
|
846
|
-
|
|
847
|
-
node_executions.append({
|
|
848
|
-
"step": turn_idx,
|
|
849
|
-
"nodes": turn_nodes,
|
|
850
|
-
"messages": turn_messages,
|
|
851
|
-
})
|
|
852
|
-
elif not turn_boundaries:
|
|
853
|
-
# Fallback: no turn boundaries detected, use simple messages
|
|
854
|
-
pass
|
|
855
|
-
|
|
856
|
-
result["node_executions"] = node_executions
|
|
857
|
-
|
|
858
708
|
return result
|
|
859
709
|
|
|
860
710
|
except HTTPException:
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/.idea/agent_api_server.iml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/call_a2a_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/a2a_bridge/redis_task_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/config_center/config_center.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/dynamic_llm/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/dynamic_llm/dynamic_llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/nats_listener.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/listener/redis_listener.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/mcp_convert/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/mcp_convert/mcp_convert.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/agent_registry.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/modes/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/modes/chain.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/orchestrator/orchestrator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/service_hub/service_hub.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/service_hub/service_hub_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/services/config_builder.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/services/stream_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/detect_message.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/shared/get_model_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/__init__.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/client.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/credential.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sdk/encoding.py
RENAMED
|
File without changes
|
{agent_api_server-2.1.10 → agent_api_server-2.1.12}/agent_api_server/sso_service/sso_service.py
RENAMED
|
File without changes
|
|
File without changes
|