@inkeep/agents-run-api 0.0.0-dev-20250910232631
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.
- package/LICENSE.md +49 -0
- package/README.md +117 -0
- package/dist/__tests__/setup.d.ts +4 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +80 -0
- package/dist/__tests__/utils/testProject.d.ts +18 -0
- package/dist/__tests__/utils/testProject.d.ts.map +1 -0
- package/dist/__tests__/utils/testProject.js +26 -0
- package/dist/__tests__/utils/testRequest.d.ts +8 -0
- package/dist/__tests__/utils/testRequest.d.ts.map +1 -0
- package/dist/__tests__/utils/testRequest.js +32 -0
- package/dist/__tests__/utils/testTenant.d.ts +64 -0
- package/dist/__tests__/utils/testTenant.d.ts.map +1 -0
- package/dist/__tests__/utils/testTenant.js +71 -0
- package/dist/a2a/client.d.ts +182 -0
- package/dist/a2a/client.d.ts.map +1 -0
- package/dist/a2a/client.js +645 -0
- package/dist/a2a/handlers.d.ts +4 -0
- package/dist/a2a/handlers.d.ts.map +1 -0
- package/dist/a2a/handlers.js +656 -0
- package/dist/a2a/transfer.d.ts +18 -0
- package/dist/a2a/transfer.d.ts.map +1 -0
- package/dist/a2a/transfer.js +22 -0
- package/dist/a2a/types.d.ts +63 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +1 -0
- package/dist/agents/Agent.d.ts +151 -0
- package/dist/agents/Agent.d.ts.map +1 -0
- package/dist/agents/Agent.js +1164 -0
- package/dist/agents/ModelFactory.d.ts +62 -0
- package/dist/agents/ModelFactory.d.ts.map +1 -0
- package/dist/agents/ModelFactory.js +208 -0
- package/dist/agents/SystemPromptBuilder.d.ts +14 -0
- package/dist/agents/SystemPromptBuilder.d.ts.map +1 -0
- package/dist/agents/SystemPromptBuilder.js +62 -0
- package/dist/agents/ToolSessionManager.d.ts +53 -0
- package/dist/agents/ToolSessionManager.d.ts.map +1 -0
- package/dist/agents/ToolSessionManager.js +106 -0
- package/dist/agents/artifactTools.d.ts +30 -0
- package/dist/agents/artifactTools.d.ts.map +1 -0
- package/dist/agents/artifactTools.js +463 -0
- package/dist/agents/generateTaskHandler.d.ts +41 -0
- package/dist/agents/generateTaskHandler.d.ts.map +1 -0
- package/dist/agents/generateTaskHandler.js +350 -0
- package/dist/agents/relationTools.d.ts +35 -0
- package/dist/agents/relationTools.d.ts.map +1 -0
- package/dist/agents/relationTools.js +246 -0
- package/dist/agents/types.d.ts +23 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/versions/V1Config.d.ts +21 -0
- package/dist/agents/versions/V1Config.d.ts.map +1 -0
- package/dist/agents/versions/V1Config.js +285 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +219 -0
- package/dist/data/agentGraph.d.ts +4 -0
- package/dist/data/agentGraph.d.ts.map +1 -0
- package/dist/data/agentGraph.js +73 -0
- package/dist/data/agents.d.ts +4 -0
- package/dist/data/agents.d.ts.map +1 -0
- package/dist/data/agents.js +78 -0
- package/dist/data/conversations.d.ts +59 -0
- package/dist/data/conversations.d.ts.map +1 -0
- package/dist/data/conversations.js +216 -0
- package/dist/data/db/clean.d.ts +6 -0
- package/dist/data/db/clean.d.ts.map +1 -0
- package/dist/data/db/clean.js +77 -0
- package/dist/data/db/dbClient.d.ts +3 -0
- package/dist/data/db/dbClient.d.ts.map +1 -0
- package/dist/data/db/dbClient.js +13 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +64 -0
- package/dist/handlers/executionHandler.d.ts +36 -0
- package/dist/handlers/executionHandler.d.ts.map +1 -0
- package/dist/handlers/executionHandler.js +415 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/instrumentation.d.ts +13 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +66 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/middleware/api-key-auth.d.ts +22 -0
- package/dist/middleware/api-key-auth.d.ts.map +1 -0
- package/dist/middleware/api-key-auth.js +139 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/openapi.d.ts +2 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +36 -0
- package/dist/routes/agents.d.ts +10 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +158 -0
- package/dist/routes/chat.d.ts +10 -0
- package/dist/routes/chat.d.ts.map +1 -0
- package/dist/routes/chat.js +307 -0
- package/dist/routes/chatDataStream.d.ts +10 -0
- package/dist/routes/chatDataStream.d.ts.map +1 -0
- package/dist/routes/chatDataStream.js +185 -0
- package/dist/routes/mcp.d.ts +10 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +500 -0
- package/dist/tracer.d.ts +24 -0
- package/dist/tracer.d.ts.map +1 -0
- package/dist/tracer.js +107 -0
- package/dist/types/chat.d.ts +25 -0
- package/dist/types/chat.d.ts.map +1 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/execution-context.d.ts +14 -0
- package/dist/types/execution-context.d.ts.map +1 -0
- package/dist/types/execution-context.js +14 -0
- package/dist/utils/agent-operations.d.ts +93 -0
- package/dist/utils/agent-operations.d.ts.map +1 -0
- package/dist/utils/agent-operations.js +78 -0
- package/dist/utils/artifact-component-schema.d.ts +29 -0
- package/dist/utils/artifact-component-schema.d.ts.map +1 -0
- package/dist/utils/artifact-component-schema.js +119 -0
- package/dist/utils/artifact-parser.d.ts +71 -0
- package/dist/utils/artifact-parser.d.ts.map +1 -0
- package/dist/utils/artifact-parser.js +253 -0
- package/dist/utils/cleanup.d.ts +19 -0
- package/dist/utils/cleanup.d.ts.map +1 -0
- package/dist/utils/cleanup.js +66 -0
- package/dist/utils/data-component-schema.d.ts +6 -0
- package/dist/utils/data-component-schema.d.ts.map +1 -0
- package/dist/utils/data-component-schema.js +43 -0
- package/dist/utils/graph-session.d.ts +230 -0
- package/dist/utils/graph-session.d.ts.map +1 -0
- package/dist/utils/graph-session.js +1199 -0
- package/dist/utils/incremental-stream-parser.d.ts +62 -0
- package/dist/utils/incremental-stream-parser.d.ts.map +1 -0
- package/dist/utils/incremental-stream-parser.js +330 -0
- package/dist/utils/response-formatter.d.ts +26 -0
- package/dist/utils/response-formatter.d.ts.map +1 -0
- package/dist/utils/response-formatter.js +158 -0
- package/dist/utils/stream-helpers.d.ts +186 -0
- package/dist/utils/stream-helpers.d.ts.map +1 -0
- package/dist/utils/stream-helpers.js +603 -0
- package/dist/utils/stream-registry.d.ts +18 -0
- package/dist/utils/stream-registry.d.ts.map +1 -0
- package/dist/utils/stream-registry.js +33 -0
- package/package.json +95 -0
- package/templates/v1/artifact.xml +7 -0
- package/templates/v1/data-component.xml +9 -0
- package/templates/v1/system-prompt.xml +52 -0
- package/templates/v1/thinking-preparation.xml +34 -0
- package/templates/v1/tool.xml +12 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
|
|
2
|
+
import { contextValidationMiddleware, createMessage, createOrGetConversation, getActiveAgentForConversation, getAgentById, getAgentGraphWithDefaultAgent, getFullGraph, getRequestExecutionContext, handleContextResolution, setActiveAgentForConversation, } from '@inkeep/agents-core';
|
|
3
|
+
// import { Hono } from 'hono';
|
|
4
|
+
import { trace } from '@opentelemetry/api';
|
|
5
|
+
import { streamSSE } from 'hono/streaming';
|
|
6
|
+
import { nanoid } from 'nanoid';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import dbClient from '../data/db/dbClient';
|
|
9
|
+
import { ExecutionHandler } from '../handlers/executionHandler';
|
|
10
|
+
import { getLogger } from '../logger';
|
|
11
|
+
import { createSSEStreamHelper } from '../utils/stream-helpers';
|
|
12
|
+
const app = new OpenAPIHono();
|
|
13
|
+
const logger = getLogger('completionsHandler');
|
|
14
|
+
// Define the OpenAPI route schema
|
|
15
|
+
const chatCompletionsRoute = createRoute({
|
|
16
|
+
method: 'post',
|
|
17
|
+
path: '/completions',
|
|
18
|
+
tags: ['chat'],
|
|
19
|
+
summary: 'Create chat completion',
|
|
20
|
+
description: 'Creates a new chat completion with streaming SSE response using the configured agent graph',
|
|
21
|
+
security: [{ bearerAuth: [] }],
|
|
22
|
+
request: {
|
|
23
|
+
body: {
|
|
24
|
+
content: {
|
|
25
|
+
'application/json': {
|
|
26
|
+
schema: z.object({
|
|
27
|
+
model: z.string().describe('The model to use for the completion'),
|
|
28
|
+
messages: z
|
|
29
|
+
.array(z.object({
|
|
30
|
+
role: z
|
|
31
|
+
.enum(['system', 'user', 'assistant', 'function', 'tool'])
|
|
32
|
+
.describe('The role of the message'),
|
|
33
|
+
content: z
|
|
34
|
+
.union([
|
|
35
|
+
z.string(),
|
|
36
|
+
z.array(z.strictObject({
|
|
37
|
+
type: z.string(),
|
|
38
|
+
text: z.string().optional(),
|
|
39
|
+
})),
|
|
40
|
+
])
|
|
41
|
+
.describe('The message content'),
|
|
42
|
+
name: z.string().optional().describe('The name of the message sender'),
|
|
43
|
+
}))
|
|
44
|
+
.describe('The conversation messages'),
|
|
45
|
+
temperature: z.number().optional().describe('Controls randomness (0-1)'),
|
|
46
|
+
top_p: z.number().optional().describe('Controls nucleus sampling'),
|
|
47
|
+
n: z.number().optional().describe('Number of completions to generate'),
|
|
48
|
+
stream: z.boolean().optional().describe('Whether to stream the response'),
|
|
49
|
+
max_tokens: z.number().optional().describe('Maximum tokens to generate'),
|
|
50
|
+
presence_penalty: z.number().optional().describe('Presence penalty (-2 to 2)'),
|
|
51
|
+
frequency_penalty: z.number().optional().describe('Frequency penalty (-2 to 2)'),
|
|
52
|
+
logit_bias: z.record(z.string(), z.number()).optional().describe('Token logit bias'),
|
|
53
|
+
user: z.string().optional().describe('User identifier'),
|
|
54
|
+
conversationId: z.string().optional().describe('Conversation ID for multi-turn chat'),
|
|
55
|
+
tools: z.array(z.string()).optional().describe('Available tools'),
|
|
56
|
+
runConfig: z.record(z.string(), z.unknown()).optional().describe('Run configuration'),
|
|
57
|
+
requestContext: z
|
|
58
|
+
.record(z.string(), z.unknown())
|
|
59
|
+
.optional()
|
|
60
|
+
.describe('Context data for template processing (validated against context config schema)'),
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
responses: {
|
|
67
|
+
200: {
|
|
68
|
+
description: 'Streaming chat completion response in Server-Sent Events format',
|
|
69
|
+
headers: z.object({
|
|
70
|
+
'Content-Type': z.string().default('text/event-stream'),
|
|
71
|
+
'Cache-Control': z.string().default('no-cache'),
|
|
72
|
+
Connection: z.string().default('keep-alive'),
|
|
73
|
+
}),
|
|
74
|
+
content: {
|
|
75
|
+
'text/event-stream': {
|
|
76
|
+
schema: z.string().describe('Server-Sent Events stream with chat completion chunks'),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
400: {
|
|
81
|
+
description: 'Invalid request context or parameters',
|
|
82
|
+
content: {
|
|
83
|
+
'application/json': {
|
|
84
|
+
schema: z.object({
|
|
85
|
+
error: z.string(),
|
|
86
|
+
details: z
|
|
87
|
+
.array(z.object({
|
|
88
|
+
field: z.string(),
|
|
89
|
+
message: z.string(),
|
|
90
|
+
value: z.unknown().optional(),
|
|
91
|
+
}))
|
|
92
|
+
.optional(),
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
404: {
|
|
98
|
+
description: 'Agent graph or agent not found',
|
|
99
|
+
content: {
|
|
100
|
+
'application/json': {
|
|
101
|
+
schema: z.object({
|
|
102
|
+
error: z.string(),
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
500: {
|
|
108
|
+
description: 'Internal server error',
|
|
109
|
+
content: {
|
|
110
|
+
'application/json': {
|
|
111
|
+
schema: z.object({
|
|
112
|
+
error: z.string(),
|
|
113
|
+
message: z.string(),
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
// Apply context validation middleware
|
|
121
|
+
app.use('/completions', contextValidationMiddleware(dbClient));
|
|
122
|
+
app.openapi(chatCompletionsRoute, async (c) => {
|
|
123
|
+
getLogger('chat').info({
|
|
124
|
+
path: c.req.path,
|
|
125
|
+
method: c.req.method,
|
|
126
|
+
params: c.req.param(),
|
|
127
|
+
}, 'Chat route accessed');
|
|
128
|
+
const otelHeaders = {
|
|
129
|
+
traceparent: c.req.header('traceparent'),
|
|
130
|
+
tracestate: c.req.header('tracestate'),
|
|
131
|
+
baggage: c.req.header('baggage'),
|
|
132
|
+
};
|
|
133
|
+
logger.info({
|
|
134
|
+
otelHeaders,
|
|
135
|
+
path: c.req.path,
|
|
136
|
+
method: c.req.method,
|
|
137
|
+
}, 'OpenTelemetry headers: chat');
|
|
138
|
+
try {
|
|
139
|
+
// Get execution context from API key authentication
|
|
140
|
+
const executionContext = getRequestExecutionContext(c);
|
|
141
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
142
|
+
getLogger('chat').debug({
|
|
143
|
+
tenantId,
|
|
144
|
+
graphId,
|
|
145
|
+
}, 'Extracted chat parameters from API key context');
|
|
146
|
+
// Get conversationId from request body or generate new one
|
|
147
|
+
const body = c.req.valid('json');
|
|
148
|
+
const conversationId = body.conversationId || nanoid();
|
|
149
|
+
// Get the graph from the full graph system first, fall back to legacy system
|
|
150
|
+
const fullGraph = await getFullGraph(dbClient)({
|
|
151
|
+
scopes: { tenantId, projectId },
|
|
152
|
+
graphId,
|
|
153
|
+
});
|
|
154
|
+
let agentGraph;
|
|
155
|
+
let defaultAgentId;
|
|
156
|
+
if (fullGraph) {
|
|
157
|
+
// Use full graph system
|
|
158
|
+
agentGraph = {
|
|
159
|
+
id: fullGraph.id,
|
|
160
|
+
name: fullGraph.name,
|
|
161
|
+
tenantId,
|
|
162
|
+
projectId,
|
|
163
|
+
defaultAgentId: fullGraph.defaultAgentId,
|
|
164
|
+
};
|
|
165
|
+
const agentKeys = Object.keys(fullGraph.agents || {});
|
|
166
|
+
const firstAgentId = agentKeys.length > 0 ? agentKeys[0] : '';
|
|
167
|
+
defaultAgentId = fullGraph.defaultAgentId || firstAgentId; // Use first agent if no defaultAgentId
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Fall back to legacy system
|
|
171
|
+
agentGraph = await getAgentGraphWithDefaultAgent(dbClient)({
|
|
172
|
+
scopes: { tenantId, projectId },
|
|
173
|
+
graphId,
|
|
174
|
+
});
|
|
175
|
+
if (!agentGraph) {
|
|
176
|
+
return c.json({ error: 'Agent graph not found' }, 404);
|
|
177
|
+
}
|
|
178
|
+
defaultAgentId = agentGraph.defaultAgentId || '';
|
|
179
|
+
}
|
|
180
|
+
if (!defaultAgentId) {
|
|
181
|
+
return c.json({ error: 'No default agent found in graph' }, 404);
|
|
182
|
+
}
|
|
183
|
+
// Get or create conversation with the default agent
|
|
184
|
+
await createOrGetConversation(dbClient)({
|
|
185
|
+
tenantId,
|
|
186
|
+
projectId,
|
|
187
|
+
id: conversationId,
|
|
188
|
+
activeAgentId: defaultAgentId,
|
|
189
|
+
});
|
|
190
|
+
const activeAgent = await getActiveAgentForConversation(dbClient)({
|
|
191
|
+
scopes: { tenantId, projectId },
|
|
192
|
+
conversationId,
|
|
193
|
+
});
|
|
194
|
+
if (!activeAgent) {
|
|
195
|
+
// Use the default agent from the graph instead of headAgentId
|
|
196
|
+
setActiveAgentForConversation(dbClient)({
|
|
197
|
+
scopes: { tenantId, projectId },
|
|
198
|
+
conversationId,
|
|
199
|
+
agentId: defaultAgentId,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
const agentId = activeAgent?.activeAgentId || defaultAgentId;
|
|
203
|
+
const agentInfo = await getAgentById(dbClient)({
|
|
204
|
+
scopes: { tenantId, projectId },
|
|
205
|
+
agentId: agentId,
|
|
206
|
+
});
|
|
207
|
+
if (!agentInfo) {
|
|
208
|
+
return c.json({ error: 'Agent not found' }, 404);
|
|
209
|
+
}
|
|
210
|
+
// Get validated context from middleware (falls back to body.context if no validation)
|
|
211
|
+
const validatedContext = c.get('validatedContext') || body.requestContext || {};
|
|
212
|
+
const credentialStores = c.get('credentialStores');
|
|
213
|
+
// Context resolution with intelligent conversation state detection
|
|
214
|
+
await handleContextResolution(tenantId, projectId, conversationId, graphId, validatedContext, dbClient, credentialStores);
|
|
215
|
+
logger.info({
|
|
216
|
+
tenantId,
|
|
217
|
+
graphId,
|
|
218
|
+
conversationId,
|
|
219
|
+
defaultAgentId,
|
|
220
|
+
activeAgentId: activeAgent?.activeAgentId || 'none',
|
|
221
|
+
hasContextConfig: !!agentGraph.contextConfigId,
|
|
222
|
+
hasRequestContext: !!body.requestContext,
|
|
223
|
+
hasValidatedContext: !!validatedContext,
|
|
224
|
+
validatedContextKeys: Object.keys(validatedContext),
|
|
225
|
+
}, 'parameters');
|
|
226
|
+
const requestId = `chatcmpl-${Date.now()}`;
|
|
227
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
228
|
+
// Extract user message for context
|
|
229
|
+
const lastUserMessage = body.messages
|
|
230
|
+
.filter((msg) => msg.role === 'user')
|
|
231
|
+
.slice(-1)[0];
|
|
232
|
+
const userMessage = lastUserMessage ? getMessageText(lastUserMessage.content) : '';
|
|
233
|
+
const messageSpan = trace.getActiveSpan();
|
|
234
|
+
if (messageSpan) {
|
|
235
|
+
messageSpan.setAttributes({
|
|
236
|
+
'message.content': userMessage,
|
|
237
|
+
'message.timestamp': Date.now(),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
// Store the user message in the database
|
|
241
|
+
await createMessage(dbClient)({
|
|
242
|
+
id: nanoid(),
|
|
243
|
+
tenantId,
|
|
244
|
+
projectId,
|
|
245
|
+
conversationId,
|
|
246
|
+
role: 'user',
|
|
247
|
+
content: {
|
|
248
|
+
text: userMessage,
|
|
249
|
+
},
|
|
250
|
+
visibility: 'user-facing',
|
|
251
|
+
messageType: 'chat',
|
|
252
|
+
});
|
|
253
|
+
if (messageSpan) {
|
|
254
|
+
messageSpan.addEvent('user.message.stored', {
|
|
255
|
+
'message.id': conversationId,
|
|
256
|
+
'database.operation': 'insert',
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// Use Hono's streamSSE helper for proper SSE formatting
|
|
260
|
+
return streamSSE(c, async (stream) => {
|
|
261
|
+
// Create SSE stream helper
|
|
262
|
+
const sseHelper = createSSEStreamHelper(stream, requestId, timestamp);
|
|
263
|
+
// Start with the role
|
|
264
|
+
await sseHelper.writeRole();
|
|
265
|
+
logger.info({ agentId }, 'Starting execution');
|
|
266
|
+
// Use the execution handler
|
|
267
|
+
const executionHandler = new ExecutionHandler();
|
|
268
|
+
const result = await executionHandler.execute({
|
|
269
|
+
executionContext,
|
|
270
|
+
conversationId,
|
|
271
|
+
userMessage,
|
|
272
|
+
initialAgentId: agentId,
|
|
273
|
+
requestId,
|
|
274
|
+
sseHelper,
|
|
275
|
+
});
|
|
276
|
+
logger.info({ result }, `Execution completed: ${result.success ? 'success' : 'failed'} after ${result.iterations} iterations`);
|
|
277
|
+
if (!result.success) {
|
|
278
|
+
// If execution failed and no error was already streamed, send a default error
|
|
279
|
+
await sseHelper.writeError('Sorry, I was unable to process your request at this time. Please try again.');
|
|
280
|
+
}
|
|
281
|
+
// Complete the stream
|
|
282
|
+
await sseHelper.complete();
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
console.error('❌ Error in chat completions endpoint:', {
|
|
287
|
+
error: error instanceof Error ? error.message : error,
|
|
288
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
289
|
+
});
|
|
290
|
+
return c.json({
|
|
291
|
+
error: 'Failed to process chat completion',
|
|
292
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
293
|
+
}, 500);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
// Helper function to extract text from content
|
|
297
|
+
const getMessageText = (content) => {
|
|
298
|
+
if (typeof content === 'string') {
|
|
299
|
+
return content;
|
|
300
|
+
}
|
|
301
|
+
// For content arrays, extract text from all text items
|
|
302
|
+
return content
|
|
303
|
+
.filter((item) => item.type === 'text' && item.text)
|
|
304
|
+
.map((item) => item.text)
|
|
305
|
+
.join(' ');
|
|
306
|
+
};
|
|
307
|
+
export default app;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OpenAPIHono } from '@hono/zod-openapi';
|
|
2
|
+
import { type CredentialStoreRegistry } from '@inkeep/agents-core';
|
|
3
|
+
type AppVariables = {
|
|
4
|
+
credentialStores: CredentialStoreRegistry;
|
|
5
|
+
};
|
|
6
|
+
declare const app: OpenAPIHono<{
|
|
7
|
+
Variables: AppVariables;
|
|
8
|
+
}, {}, "/">;
|
|
9
|
+
export default app;
|
|
10
|
+
//# sourceMappingURL=chatDataStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatDataStream.d.ts","sourceRoot":"","sources":["../../src/routes/chatDataStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,KAAK,uBAAuB,EAU7B,MAAM,qBAAqB,CAAC;AAU7B,KAAK,YAAY,GAAG;IAClB,gBAAgB,EAAE,uBAAuB,CAAC;CAC3C,CAAC;AAEF,QAAA,MAAM,GAAG;eAAgC,YAAY;WAAK,CAAC;AA4M3D,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
|
|
2
|
+
import { commonGetErrorResponses, contextValidationMiddleware, createMessage, getActiveAgentForConversation, getAgentById, getAgentGraphWithDefaultAgent, getRequestExecutionContext, handleContextResolution, setActiveAgentForConversation, } from '@inkeep/agents-core';
|
|
3
|
+
import { trace } from '@opentelemetry/api';
|
|
4
|
+
import { createUIMessageStream, JsonToSseTransformStream } from 'ai';
|
|
5
|
+
import { stream } from 'hono/streaming';
|
|
6
|
+
import { nanoid } from 'nanoid';
|
|
7
|
+
import dbClient from '../data/db/dbClient';
|
|
8
|
+
import { ExecutionHandler } from '../handlers/executionHandler';
|
|
9
|
+
import { getLogger } from '../logger';
|
|
10
|
+
import { createVercelStreamHelper } from '../utils/stream-helpers';
|
|
11
|
+
const app = new OpenAPIHono();
|
|
12
|
+
const logger = getLogger('chatDataStream');
|
|
13
|
+
const chatDataStreamRoute = createRoute({
|
|
14
|
+
method: 'post',
|
|
15
|
+
path: '/chat',
|
|
16
|
+
tags: ['chat'],
|
|
17
|
+
summary: 'Chat (Vercel Streaming Protocol)',
|
|
18
|
+
description: 'Chat completion endpoint streaming with Vercel data stream protocol.',
|
|
19
|
+
security: [{ bearerAuth: [] }],
|
|
20
|
+
request: {
|
|
21
|
+
body: {
|
|
22
|
+
content: {
|
|
23
|
+
'application/json': {
|
|
24
|
+
schema: z.object({
|
|
25
|
+
model: z.string().optional(),
|
|
26
|
+
messages: z.array(z.object({
|
|
27
|
+
role: z.enum(['system', 'user', 'assistant', 'function', 'tool']),
|
|
28
|
+
content: z.any(),
|
|
29
|
+
parts: z
|
|
30
|
+
.array(z.object({
|
|
31
|
+
type: z.union([
|
|
32
|
+
z.enum(['text', 'image', 'audio', 'video', 'file']),
|
|
33
|
+
z.string().regex(/^data-/, 'Type must start with "data-"'),
|
|
34
|
+
]),
|
|
35
|
+
text: z.string().optional(),
|
|
36
|
+
}))
|
|
37
|
+
.optional(),
|
|
38
|
+
})),
|
|
39
|
+
id: z.string().optional(),
|
|
40
|
+
conversationId: z.string().optional(),
|
|
41
|
+
requestContext: z
|
|
42
|
+
.record(z.string(), z.unknown())
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Context data for template processing'),
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
responses: {
|
|
51
|
+
200: {
|
|
52
|
+
description: 'Streamed chat completion',
|
|
53
|
+
headers: z.object({
|
|
54
|
+
'Content-Type': z.string().default('text/plain; charset=utf-8'),
|
|
55
|
+
'x-vercel-ai-data-stream': z.string().default('v1'),
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
...commonGetErrorResponses,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
// Apply context validation middleware
|
|
62
|
+
app.use('/chat', contextValidationMiddleware(dbClient));
|
|
63
|
+
app.openapi(chatDataStreamRoute, async (c) => {
|
|
64
|
+
try {
|
|
65
|
+
// Get execution context from API key authentication
|
|
66
|
+
const executionContext = getRequestExecutionContext(c);
|
|
67
|
+
const { tenantId, projectId, graphId } = executionContext;
|
|
68
|
+
const body = await c.req.valid('json');
|
|
69
|
+
const conversationId = body.conversationId || nanoid();
|
|
70
|
+
// Add conversation ID to parent span
|
|
71
|
+
const activeSpan = trace.getActiveSpan();
|
|
72
|
+
if (activeSpan) {
|
|
73
|
+
activeSpan.setAttributes({
|
|
74
|
+
'conversation.id': conversationId,
|
|
75
|
+
'tenant.id': tenantId,
|
|
76
|
+
'graph.id': graphId,
|
|
77
|
+
'project.id': projectId,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const agentGraph = await getAgentGraphWithDefaultAgent(dbClient)({
|
|
81
|
+
scopes: { tenantId, projectId },
|
|
82
|
+
graphId,
|
|
83
|
+
});
|
|
84
|
+
if (!agentGraph) {
|
|
85
|
+
return c.json({ error: 'Agent graph not found' }, 404);
|
|
86
|
+
}
|
|
87
|
+
const defaultAgentId = agentGraph.defaultAgentId;
|
|
88
|
+
const graphName = agentGraph.name;
|
|
89
|
+
const activeAgent = await getActiveAgentForConversation(dbClient)({
|
|
90
|
+
scopes: { tenantId, projectId },
|
|
91
|
+
conversationId,
|
|
92
|
+
});
|
|
93
|
+
if (!activeAgent) {
|
|
94
|
+
setActiveAgentForConversation(dbClient)({
|
|
95
|
+
scopes: { tenantId, projectId },
|
|
96
|
+
conversationId,
|
|
97
|
+
agentId: defaultAgentId,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const agentId = activeAgent?.activeAgentId || defaultAgentId;
|
|
101
|
+
const agentInfo = await getAgentById(dbClient)({
|
|
102
|
+
scopes: { tenantId, projectId },
|
|
103
|
+
agentId: agentId,
|
|
104
|
+
});
|
|
105
|
+
if (!agentInfo) {
|
|
106
|
+
return c.json({ error: 'Agent not found' }, 404);
|
|
107
|
+
}
|
|
108
|
+
// Get validated context from middleware (falls back to body.context if no validation)
|
|
109
|
+
const validatedContext = c.get('validatedContext') || body.requestContext || {};
|
|
110
|
+
const credentialStores = c.get('credentialStores');
|
|
111
|
+
// Context resolution with intelligent conversation state detection
|
|
112
|
+
await handleContextResolution(tenantId, projectId, conversationId, graphId, validatedContext, dbClient, credentialStores);
|
|
113
|
+
// Store last user message
|
|
114
|
+
const lastUserMessage = body.messages.filter((m) => m.role === 'user').slice(-1)[0];
|
|
115
|
+
const userText = typeof lastUserMessage?.content === 'string'
|
|
116
|
+
? lastUserMessage.content
|
|
117
|
+
: lastUserMessage?.parts?.map((p) => p.text).join('') || '';
|
|
118
|
+
logger.info({ userText, lastUserMessage }, 'userText');
|
|
119
|
+
const messageSpan = trace.getActiveSpan();
|
|
120
|
+
if (messageSpan) {
|
|
121
|
+
messageSpan.setAttributes({
|
|
122
|
+
'message.timestamp': new Date().toISOString(),
|
|
123
|
+
'message.content': userText,
|
|
124
|
+
'graph.name': graphName,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
await createMessage(dbClient)({
|
|
128
|
+
id: nanoid(),
|
|
129
|
+
tenantId,
|
|
130
|
+
projectId,
|
|
131
|
+
conversationId,
|
|
132
|
+
role: 'user',
|
|
133
|
+
content: { text: userText },
|
|
134
|
+
visibility: 'user-facing',
|
|
135
|
+
messageType: 'chat',
|
|
136
|
+
});
|
|
137
|
+
if (messageSpan) {
|
|
138
|
+
messageSpan.addEvent('user.message.stored', {
|
|
139
|
+
'message.id': conversationId,
|
|
140
|
+
'database.operation': 'insert',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Create UI Message Stream using AI SDK V5
|
|
144
|
+
const dataStream = createUIMessageStream({
|
|
145
|
+
execute: async ({ writer }) => {
|
|
146
|
+
const streamHelper = createVercelStreamHelper(writer);
|
|
147
|
+
try {
|
|
148
|
+
const executionHandler = new ExecutionHandler();
|
|
149
|
+
const result = await executionHandler.execute({
|
|
150
|
+
executionContext,
|
|
151
|
+
conversationId,
|
|
152
|
+
userMessage: userText,
|
|
153
|
+
initialAgentId: agentId,
|
|
154
|
+
requestId: `chatds-${Date.now()}`,
|
|
155
|
+
sseHelper: streamHelper,
|
|
156
|
+
});
|
|
157
|
+
if (!result.success) {
|
|
158
|
+
await streamHelper.writeError('Unable to process request');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
logger.error({ err }, 'Streaming error');
|
|
163
|
+
await streamHelper.writeError('Internal server error');
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
// Clean up stream helper resources if it has cleanup method
|
|
167
|
+
if ('cleanup' in streamHelper && typeof streamHelper.cleanup === 'function') {
|
|
168
|
+
streamHelper.cleanup();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
c.header('content-type', 'text/event-stream');
|
|
174
|
+
c.header('cache-control', 'no-cache');
|
|
175
|
+
c.header('connection', 'keep-alive');
|
|
176
|
+
c.header('x-vercel-ai-data-stream', 'v2');
|
|
177
|
+
c.header('x-accel-buffering', 'no'); // disable nginx buffering
|
|
178
|
+
return stream(c, (stream) => stream.pipe(dataStream.pipeThrough(new JsonToSseTransformStream()).pipeThrough(new TextEncoderStream())));
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
logger.error({ error }, 'chatDataStream error');
|
|
182
|
+
return c.json({ error: 'Failed to process chat completion' }, 500);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
export default app;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OpenAPIHono } from '@hono/zod-openapi';
|
|
2
|
+
import { type CredentialStoreRegistry } from '@inkeep/agents-core';
|
|
3
|
+
type AppVariables = {
|
|
4
|
+
credentialStores: CredentialStoreRegistry;
|
|
5
|
+
};
|
|
6
|
+
declare const app: OpenAPIHono<{
|
|
7
|
+
Variables: AppVariables;
|
|
8
|
+
}, {}, "/">;
|
|
9
|
+
export default app;
|
|
10
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAa7D,OAAO,EACL,KAAK,uBAAuB,EAS7B,MAAM,qBAAqB,CAAC;AAgX7B,KAAK,YAAY,GAAG;IAClB,gBAAgB,EAAE,uBAAuB,CAAC;CAC3C,CAAC;AAEF,QAAA,MAAM,GAAG;eAAgC,YAAY;WAAK,CAAC;AA6T3D,eAAe,GAAG,CAAC"}
|