@polpo-ai/server 0.15.60 → 0.15.63
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/dist/channels/conversation-bridge.d.ts +50 -0
- package/dist/channels/conversation-bridge.d.ts.map +1 -0
- package/dist/channels/conversation-bridge.js +234 -0
- package/dist/channels/conversation-bridge.js.map +1 -0
- package/dist/channels/conversation-bridge.test.d.ts +2 -0
- package/dist/channels/conversation-bridge.test.d.ts.map +1 -0
- package/dist/channels/conversation-bridge.test.js +219 -0
- package/dist/channels/conversation-bridge.test.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/completions/agent-step-runner.js +2 -2
- package/dist/routes/completions/agent-step-runner.js.map +1 -1
- package/dist/routes/completions/agent-step-runner.test.js +1 -1
- package/dist/routes/completions/agent-step-runner.test.js.map +1 -1
- package/dist/routes/completions/chat-handler.d.ts +13 -2
- package/dist/routes/completions/chat-handler.d.ts.map +1 -1
- package/dist/routes/completions/chat-handler.js +61 -18
- package/dist/routes/completions/chat-handler.js.map +1 -1
- package/dist/routes/completions/chat-interaction-runtime.d.ts +5 -0
- package/dist/routes/completions/chat-interaction-runtime.d.ts.map +1 -0
- package/dist/routes/completions/chat-interaction-runtime.js +46 -0
- package/dist/routes/completions/chat-interaction-runtime.js.map +1 -0
- package/dist/routes/completions/chat-suggestions.d.ts +31 -0
- package/dist/routes/completions/chat-suggestions.d.ts.map +1 -0
- package/dist/routes/completions/chat-suggestions.js +147 -0
- package/dist/routes/completions/chat-suggestions.js.map +1 -0
- package/dist/routes/completions/chat-suggestions.test.d.ts +2 -0
- package/dist/routes/completions/chat-suggestions.test.d.ts.map +1 -0
- package/dist/routes/completions/chat-suggestions.test.js +100 -0
- package/dist/routes/completions/chat-suggestions.test.js.map +1 -0
- package/dist/routes/completions/chat-via-run-handler.d.ts +5 -0
- package/dist/routes/completions/chat-via-run-handler.d.ts.map +1 -1
- package/dist/routes/completions/chat-via-run-handler.js +54 -19
- package/dist/routes/completions/chat-via-run-handler.js.map +1 -1
- package/dist/routes/completions/chat-via-run-handler.test.js +251 -6
- package/dist/routes/completions/chat-via-run-handler.test.js.map +1 -1
- package/dist/routes/completions/conversation-turn.d.ts.map +1 -1
- package/dist/routes/completions/conversation-turn.js +19 -2
- package/dist/routes/completions/conversation-turn.js.map +1 -1
- package/dist/routes/completions/message-mapping.d.ts.map +1 -1
- package/dist/routes/completions/message-mapping.js +7 -24
- package/dist/routes/completions/message-mapping.js.map +1 -1
- package/dist/routes/completions/runtime-plan-hook.test.js +43 -0
- package/dist/routes/completions/runtime-plan-hook.test.js.map +1 -1
- package/dist/routes/completions/schemas.d.ts +94 -3
- package/dist/routes/completions/schemas.d.ts.map +1 -1
- package/dist/routes/completions/schemas.js +52 -0
- package/dist/routes/completions/schemas.js.map +1 -1
- package/dist/routes/completions/schemas.test.js +118 -0
- package/dist/routes/completions/schemas.test.js.map +1 -1
- package/dist/routes/completions/sse.d.ts +9 -1
- package/dist/routes/completions/sse.d.ts.map +1 -1
- package/dist/routes/completions/sse.js +28 -0
- package/dist/routes/completions/sse.js.map +1 -1
- package/dist/routes/completions/structured-output.d.ts +20 -0
- package/dist/routes/completions/structured-output.d.ts.map +1 -0
- package/dist/routes/completions/structured-output.js +82 -0
- package/dist/routes/completions/structured-output.js.map +1 -0
- package/dist/routes/completions/structured-output.test.d.ts +2 -0
- package/dist/routes/completions/structured-output.test.d.ts.map +1 -0
- package/dist/routes/completions/structured-output.test.js +54 -0
- package/dist/routes/completions/structured-output.test.js.map +1 -0
- package/dist/routes/completions/tool-disclosure.d.ts +1 -1
- package/dist/routes/completions/tool-disclosure.d.ts.map +1 -1
- package/dist/routes/completions/tool-disclosure.js +18 -18
- package/dist/routes/completions/tool-disclosure.js.map +1 -1
- package/dist/routes/completions/tool-disclosure.test.js +29 -29
- package/dist/routes/completions/tool-disclosure.test.js.map +1 -1
- package/dist/routes/completions/tool-mapping.d.ts +8 -0
- package/dist/routes/completions/tool-mapping.d.ts.map +1 -1
- package/dist/routes/completions/tool-mapping.js +10 -0
- package/dist/routes/completions/tool-mapping.js.map +1 -1
- package/dist/routes/completions/tool-mapping.test.js +10 -0
- package/dist/routes/completions/tool-mapping.test.js.map +1 -1
- package/dist/routes/completions.d.ts +16 -1
- package/dist/routes/completions.d.ts.map +1 -1
- package/dist/routes/completions.js.map +1 -1
- package/package.json +8 -7
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { normalizeChatInteractionSettings, } from "@polpo-ai/core/chat-interactions";
|
|
2
|
+
import { completionResolvedModelInfo } from "./agent-step-runner.js";
|
|
3
|
+
import { generateChatSuggestions } from "./chat-suggestions.js";
|
|
4
|
+
import { isStructuredResponseFormat } from "./structured-output.js";
|
|
5
|
+
/** Run the optional, fail-open suggestion pass for a successful text turn. */
|
|
6
|
+
export async function suggestionsForCompletion(execution, finalText, signal) {
|
|
7
|
+
if (!execution.interactionCapabilities?.suggestions
|
|
8
|
+
|| isStructuredResponseFormat(execution.body.response_format)
|
|
9
|
+
|| !finalText.trim()
|
|
10
|
+
|| signal?.aborted) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
let settings;
|
|
14
|
+
try {
|
|
15
|
+
settings = normalizeChatInteractionSettings(execution.deps.getConfig()?.settings?.chat).suggestions;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const result = await generateChatSuggestions({
|
|
21
|
+
model: execution.m.aiModel,
|
|
22
|
+
providerOptions: execution.providerOpts,
|
|
23
|
+
messages: execution.body.messages ?? [],
|
|
24
|
+
finalText,
|
|
25
|
+
maxItems: settings.maxItems,
|
|
26
|
+
guidance: settings.guidance,
|
|
27
|
+
signal,
|
|
28
|
+
});
|
|
29
|
+
if (result.usage) {
|
|
30
|
+
try {
|
|
31
|
+
execution.deps.onAuxiliaryModelFinished?.({
|
|
32
|
+
operation: "chat_suggestions",
|
|
33
|
+
usage: result.usage,
|
|
34
|
+
model: execution.m.id ?? execution.m.provider,
|
|
35
|
+
resolvedModel: completionResolvedModelInfo(execution.m),
|
|
36
|
+
agent: execution.body.agent,
|
|
37
|
+
sessionId: execution.sessionId ?? undefined,
|
|
38
|
+
user: execution.body.user,
|
|
39
|
+
providerMetadata: result.providerMetadata,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch { /* auxiliary accounting never fails the main response */ }
|
|
43
|
+
}
|
|
44
|
+
return result.suggestions;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=chat-interaction-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-interaction-runtime.js","sourceRoot":"","sources":["../../../src/routes/completions/chat-interaction-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,GAEjC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAErE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAEpE,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,SAAkC,EAClC,SAAiB,EACjB,MAAoB;IAEpB,IACE,CAAC,SAAS,CAAC,uBAAuB,EAAE,WAAW;WAC5C,0BAA0B,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC;WAC1D,CAAC,SAAS,CAAC,IAAI,EAAE;WACjB,MAAM,EAAE,OAAO,EAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,gCAAgC,CACzC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAC3C,CAAC,WAAW,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC;QAC3C,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO;QAC1B,eAAe,EAAE,SAAS,CAAC,YAAY;QACvC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE;QACvC,SAAS;QACT,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBACxC,SAAS,EAAE,kBAAkB;gBAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,QAAQ;gBAC7C,aAAa,EAAE,2BAA2B,CAAC,SAAS,CAAC,CAAC,CAAC;gBACvD,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK;gBAC3B,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,SAAS;gBAC3C,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI;gBACzB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;aAC1C,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC,CAAC,wDAAwD,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ChatSuggestion } from "@polpo-ai/core/chat-interactions";
|
|
2
|
+
import { type LanguageModel, type LanguageModelUsage } from "ai";
|
|
3
|
+
interface SuggestionGenerationResponse {
|
|
4
|
+
output?: unknown;
|
|
5
|
+
usage?: LanguageModelUsage;
|
|
6
|
+
providerMetadata?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export type ChatSuggestionGenerate = (options: Record<string, unknown>) => Promise<SuggestionGenerationResponse>;
|
|
9
|
+
export interface GenerateChatSuggestionsInput {
|
|
10
|
+
model: LanguageModel;
|
|
11
|
+
providerOptions?: Record<string, any>;
|
|
12
|
+
messages: Array<{
|
|
13
|
+
role?: unknown;
|
|
14
|
+
content?: unknown;
|
|
15
|
+
}>;
|
|
16
|
+
finalText: string;
|
|
17
|
+
maxItems: number;
|
|
18
|
+
guidance?: string;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
generate?: ChatSuggestionGenerate;
|
|
22
|
+
}
|
|
23
|
+
export interface ChatSuggestionGenerationResult {
|
|
24
|
+
suggestions: ChatSuggestion[];
|
|
25
|
+
usage?: LanguageModelUsage;
|
|
26
|
+
providerMetadata?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/** Generate optional next-message suggestions without affecting the main turn. */
|
|
29
|
+
export declare function generateChatSuggestions(input: GenerateChatSuggestionsInput): Promise<ChatSuggestionGenerationResult>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=chat-suggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-suggestions.d.ts","sourceRoot":"","sources":["../../../src/routes/completions/chat-suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAwB,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAiBvF,UAAU,4BAA4B;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,OAAO,CAAC,4BAA4B,CAAC,CAAC;AAE3C,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,aAAa,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AAED,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAuED,kFAAkF;AAClF,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,8BAA8B,CAAC,CA2DzC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { generateText, Output } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const MAX_CONTEXT_MESSAGES = 8;
|
|
4
|
+
const MAX_CONTEXT_CHARS = 7_000;
|
|
5
|
+
const MAX_FINAL_TEXT_CHARS = 4_000;
|
|
6
|
+
const MAX_LABEL_CHARS = 64;
|
|
7
|
+
const MAX_PROMPT_CHARS = 500;
|
|
8
|
+
const DEFAULT_TIMEOUT_MS = 4_000;
|
|
9
|
+
const generatedSuggestionsSchema = z.object({
|
|
10
|
+
suggestions: z.array(z.object({
|
|
11
|
+
label: z.string().min(1).max(MAX_LABEL_CHARS),
|
|
12
|
+
prompt: z.string().min(1).max(MAX_PROMPT_CHARS),
|
|
13
|
+
}).strict()).max(4),
|
|
14
|
+
}).strict();
|
|
15
|
+
function textFromContent(content) {
|
|
16
|
+
if (typeof content === "string")
|
|
17
|
+
return content;
|
|
18
|
+
if (!Array.isArray(content))
|
|
19
|
+
return "";
|
|
20
|
+
return content
|
|
21
|
+
.map((part) => {
|
|
22
|
+
if (!part || typeof part !== "object")
|
|
23
|
+
return "";
|
|
24
|
+
const value = part;
|
|
25
|
+
if (value.type === "text" && typeof value.text === "string")
|
|
26
|
+
return value.text;
|
|
27
|
+
return "";
|
|
28
|
+
})
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join("\n");
|
|
31
|
+
}
|
|
32
|
+
function boundedConversation(messages) {
|
|
33
|
+
const selected = [];
|
|
34
|
+
let remaining = MAX_CONTEXT_CHARS;
|
|
35
|
+
for (const message of messages.slice(-MAX_CONTEXT_MESSAGES).reverse()) {
|
|
36
|
+
if (remaining <= 0)
|
|
37
|
+
break;
|
|
38
|
+
if (message.role !== "user" && message.role !== "assistant")
|
|
39
|
+
continue;
|
|
40
|
+
const content = textFromContent(message.content).trim();
|
|
41
|
+
if (!content)
|
|
42
|
+
continue;
|
|
43
|
+
const bounded = content.slice(-remaining);
|
|
44
|
+
remaining -= bounded.length;
|
|
45
|
+
selected.push({ role: message.role, content: bounded });
|
|
46
|
+
}
|
|
47
|
+
return selected.reverse();
|
|
48
|
+
}
|
|
49
|
+
function compactWhitespace(value) {
|
|
50
|
+
return value.trim().replace(/\s+/g, " ");
|
|
51
|
+
}
|
|
52
|
+
function stableSuggestionId(label, prompt) {
|
|
53
|
+
let hash = 2166136261;
|
|
54
|
+
const value = `${label}\u0000${prompt}`;
|
|
55
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
56
|
+
hash ^= value.charCodeAt(index);
|
|
57
|
+
hash = Math.imul(hash, 16777619);
|
|
58
|
+
}
|
|
59
|
+
return `suggestion_${(hash >>> 0).toString(36)}`;
|
|
60
|
+
}
|
|
61
|
+
function sanitizeSuggestions(value, maxItems) {
|
|
62
|
+
const parsed = generatedSuggestionsSchema.safeParse(value);
|
|
63
|
+
if (!parsed.success)
|
|
64
|
+
return [];
|
|
65
|
+
const suggestions = [];
|
|
66
|
+
const seenPrompts = new Set();
|
|
67
|
+
for (const item of parsed.data.suggestions) {
|
|
68
|
+
const label = compactWhitespace(item.label).slice(0, MAX_LABEL_CHARS);
|
|
69
|
+
const prompt = compactWhitespace(item.prompt).slice(0, MAX_PROMPT_CHARS);
|
|
70
|
+
const key = prompt.toLocaleLowerCase();
|
|
71
|
+
if (!label || !prompt || seenPrompts.has(key))
|
|
72
|
+
continue;
|
|
73
|
+
seenPrompts.add(key);
|
|
74
|
+
suggestions.push({
|
|
75
|
+
id: stableSuggestionId(label, prompt),
|
|
76
|
+
label,
|
|
77
|
+
prompt,
|
|
78
|
+
});
|
|
79
|
+
if (suggestions.length >= maxItems)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
return suggestions;
|
|
83
|
+
}
|
|
84
|
+
/** Generate optional next-message suggestions without affecting the main turn. */
|
|
85
|
+
export async function generateChatSuggestions(input) {
|
|
86
|
+
const finalText = input.finalText.trim();
|
|
87
|
+
if (!finalText || input.signal?.aborted)
|
|
88
|
+
return { suggestions: [] };
|
|
89
|
+
const maxItems = Math.max(2, Math.min(4, Math.trunc(input.maxItems)));
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const abortFromParent = () => controller.abort(input.signal?.reason);
|
|
92
|
+
input.signal?.addEventListener("abort", abortFromParent, { once: true });
|
|
93
|
+
const timeoutMs = Math.max(1, input.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
94
|
+
let timeoutId;
|
|
95
|
+
const timeout = new Promise((_, reject) => {
|
|
96
|
+
timeoutId = setTimeout(() => {
|
|
97
|
+
controller.abort(new Error("Chat suggestion generation timed out"));
|
|
98
|
+
reject(new Error("Chat suggestion generation timed out"));
|
|
99
|
+
}, timeoutMs);
|
|
100
|
+
});
|
|
101
|
+
try {
|
|
102
|
+
const generate = input.generate
|
|
103
|
+
?? generateText;
|
|
104
|
+
const response = await Promise.race([
|
|
105
|
+
generate({
|
|
106
|
+
model: input.model,
|
|
107
|
+
system: [
|
|
108
|
+
"Suggest concise, useful next messages the user could send after this assistant response.",
|
|
109
|
+
"Each label is a short action. Each prompt is the exact standalone user message to send.",
|
|
110
|
+
"Do not repeat the assistant response. Do not invent unsupported capabilities.",
|
|
111
|
+
"Return no suggestions when there is no meaningful next step.",
|
|
112
|
+
].join(" "),
|
|
113
|
+
prompt: JSON.stringify({
|
|
114
|
+
maxItems,
|
|
115
|
+
...(input.guidance ? { guidance: input.guidance } : {}),
|
|
116
|
+
conversation: boundedConversation(input.messages),
|
|
117
|
+
assistantResponse: finalText.slice(-MAX_FINAL_TEXT_CHARS),
|
|
118
|
+
}),
|
|
119
|
+
output: Output.object({ schema: generatedSuggestionsSchema }),
|
|
120
|
+
temperature: 0.2,
|
|
121
|
+
maxOutputTokens: 384,
|
|
122
|
+
abortSignal: controller.signal,
|
|
123
|
+
...(input.providerOptions
|
|
124
|
+
? { providerOptions: input.providerOptions }
|
|
125
|
+
: {}),
|
|
126
|
+
}),
|
|
127
|
+
timeout,
|
|
128
|
+
]);
|
|
129
|
+
const suggestions = sanitizeSuggestions(response.output, maxItems);
|
|
130
|
+
return {
|
|
131
|
+
suggestions,
|
|
132
|
+
...(response.usage ? { usage: response.usage } : {}),
|
|
133
|
+
...(response.providerMetadata
|
|
134
|
+
? { providerMetadata: response.providerMetadata }
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return { suggestions: [] };
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
if (timeoutId)
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
input.signal?.removeEventListener("abort", abortFromParent);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=chat-suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-suggestions.js","sourceRoot":"","sources":["../../../src/routes/completions/chat-suggestions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-suggestions.test.d.ts","sourceRoot":"","sources":["../../../src/routes/completions/chat-suggestions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { generateChatSuggestions } from "./chat-suggestions.js";
|
|
3
|
+
const model = {};
|
|
4
|
+
describe("generateChatSuggestions", () => {
|
|
5
|
+
it("returns a bounded, kind-free public primitive", async () => {
|
|
6
|
+
const generate = vi.fn().mockResolvedValue({
|
|
7
|
+
output: {
|
|
8
|
+
suggestions: [
|
|
9
|
+
{ label: " Add tests ", prompt: " Add tests for this change. " },
|
|
10
|
+
{ label: "Duplicate", prompt: "Add tests for this change." },
|
|
11
|
+
{ label: "Review", prompt: "Review the implementation for edge cases." },
|
|
12
|
+
{ label: "Document", prompt: "Document the public API." },
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
usage: { inputTokens: 10, outputTokens: 8, totalTokens: 18 },
|
|
16
|
+
providerMetadata: { gateway: { generationId: "gen_1" } },
|
|
17
|
+
});
|
|
18
|
+
const result = await generateChatSuggestions({
|
|
19
|
+
model,
|
|
20
|
+
messages: [{ role: "user", content: "Implement this" }],
|
|
21
|
+
finalText: "The implementation is ready.",
|
|
22
|
+
maxItems: 2,
|
|
23
|
+
generate,
|
|
24
|
+
});
|
|
25
|
+
expect(result.suggestions).toHaveLength(2);
|
|
26
|
+
expect(result.suggestions[0]).toEqual({
|
|
27
|
+
id: expect.stringMatching(/^suggestion_/),
|
|
28
|
+
label: "Add tests",
|
|
29
|
+
prompt: "Add tests for this change.",
|
|
30
|
+
});
|
|
31
|
+
expect(result.suggestions[0]).not.toHaveProperty("kind");
|
|
32
|
+
expect(result.suggestions[1]?.label).toBe("Review");
|
|
33
|
+
expect(result.usage).toEqual({ inputTokens: 10, outputTokens: 8, totalTokens: 18 });
|
|
34
|
+
expect(result.providerMetadata).toEqual({ gateway: { generationId: "gen_1" } });
|
|
35
|
+
});
|
|
36
|
+
it("passes only bounded conversation text and project guidance", async () => {
|
|
37
|
+
const generate = vi.fn().mockResolvedValue({ output: { suggestions: [] } });
|
|
38
|
+
await generateChatSuggestions({
|
|
39
|
+
model,
|
|
40
|
+
messages: Array.from({ length: 20 }, (_, index) => ({
|
|
41
|
+
role: index % 2 === 0 ? "user" : "assistant",
|
|
42
|
+
content: `message-${index}-${"x".repeat(2_000)}`,
|
|
43
|
+
})),
|
|
44
|
+
finalText: "done",
|
|
45
|
+
maxItems: 3,
|
|
46
|
+
guidance: "Prefer concrete next actions.",
|
|
47
|
+
generate,
|
|
48
|
+
});
|
|
49
|
+
const call = generate.mock.calls[0]?.[0];
|
|
50
|
+
expect(String(call.prompt).length).toBeLessThan(10_000);
|
|
51
|
+
expect(String(call.prompt)).toContain("Prefer concrete next actions.");
|
|
52
|
+
expect(call).not.toHaveProperty("tools");
|
|
53
|
+
});
|
|
54
|
+
it("fails open for invalid model output", async () => {
|
|
55
|
+
const result = await generateChatSuggestions({
|
|
56
|
+
model,
|
|
57
|
+
messages: [{ role: "user", content: "hello" }],
|
|
58
|
+
finalText: "hi",
|
|
59
|
+
maxItems: 3,
|
|
60
|
+
generate: vi.fn().mockResolvedValue({ output: { suggestions: "invalid" } }),
|
|
61
|
+
});
|
|
62
|
+
expect(result).toEqual({ suggestions: [] });
|
|
63
|
+
});
|
|
64
|
+
it("fails open when the auxiliary model call rejects", async () => {
|
|
65
|
+
const result = await generateChatSuggestions({
|
|
66
|
+
model,
|
|
67
|
+
messages: [{ role: "user", content: "hello" }],
|
|
68
|
+
finalText: "hi",
|
|
69
|
+
maxItems: 3,
|
|
70
|
+
generate: vi.fn().mockRejectedValue(new Error("provider unavailable")),
|
|
71
|
+
});
|
|
72
|
+
expect(result).toEqual({ suggestions: [] });
|
|
73
|
+
});
|
|
74
|
+
it("times out without failing the main completion", async () => {
|
|
75
|
+
const result = await generateChatSuggestions({
|
|
76
|
+
model,
|
|
77
|
+
messages: [{ role: "user", content: "hello" }],
|
|
78
|
+
finalText: "hi",
|
|
79
|
+
maxItems: 3,
|
|
80
|
+
timeoutMs: 5,
|
|
81
|
+
generate: vi.fn().mockImplementation(({ abortSignal }) => new Promise((_, reject) => {
|
|
82
|
+
abortSignal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
|
|
83
|
+
})),
|
|
84
|
+
});
|
|
85
|
+
expect(result).toEqual({ suggestions: [] });
|
|
86
|
+
});
|
|
87
|
+
it("skips generation for blank assistant output", async () => {
|
|
88
|
+
const generate = vi.fn();
|
|
89
|
+
const result = await generateChatSuggestions({
|
|
90
|
+
model,
|
|
91
|
+
messages: [{ role: "user", content: "hello" }],
|
|
92
|
+
finalText: " ",
|
|
93
|
+
maxItems: 3,
|
|
94
|
+
generate,
|
|
95
|
+
});
|
|
96
|
+
expect(result).toEqual({ suggestions: [] });
|
|
97
|
+
expect(generate).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=chat-suggestions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-suggestions.test.js","sourceRoot":"","sources":["../../../src/routes/completions/chat-suggestions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,6 +91,11 @@ export declare function runNonStreamingChatViaRun(c: Context, execution: ChatCom
|
|
|
91
91
|
message: string;
|
|
92
92
|
type: "model_error";
|
|
93
93
|
code: "model_request_failed";
|
|
94
|
+
} | {
|
|
95
|
+
message: string;
|
|
96
|
+
type: "invalid_request_error";
|
|
97
|
+
code: "invalid_response_format_output";
|
|
98
|
+
param: "response_format";
|
|
94
99
|
};
|
|
95
100
|
}, any, "json">) | (Response & import("hono").TypedResponse<{
|
|
96
101
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-via-run-handler.d.ts","sourceRoot":"","sources":["../../../src/routes/completions/chat-via-run-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-via-run-handler.d.ts","sourceRoot":"","sources":["../../../src/routes/completions/chat-via-run-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAwCjE,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,uBAAuB,GAAG,oBAAoB,CA8C9F;AAiBD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAuMD,gDAAgD;AAChD,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,uBAAuB,qBAqIpF;AAED,oDAAoD;AACpD,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oEA6G7F;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,uBAAuB,EAClC,KAAK,GAAE;IAAE,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACtF,OAAO,CAAC,oBAAoB,CAAC,CA6E/B"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { streamSSE } from "hono/streaming";
|
|
2
2
|
import { agentConfigForModelAttempt, completionResolvedModelInfo, MAX_TURNS } from "./agent-step-runner.js";
|
|
3
|
-
import { completionResponse, guardrailErrorEnvelope, modelErrorEnvelope, modelNotFoundEnvelope, sseChunk, } from "./sse.js";
|
|
3
|
+
import { completionResponse, guardrailErrorEnvelope, modelErrorEnvelope, modelNotFoundEnvelope, sseChunk, ssePolpoChunk, structuredOutputErrorEnvelope, } from "./sse.js";
|
|
4
4
|
import { persistAssistantMessage, emitFileChanged, toAITools, CLIENT_SIDE_TOOLS, CLIENT_SIDE_TOOL_NAMES, } from "./tool-mapping.js";
|
|
5
|
+
import { suggestionsForCompletion } from "./chat-interaction-runtime.js";
|
|
5
6
|
import { applyCompletionOutputPolicy, streamingOutputPolicyMode, } from "./output-guardrails.js";
|
|
7
|
+
import { finalizeResponseFormatText, isStructuredResponseFormat, } from "./structured-output.js";
|
|
6
8
|
/** Best-effort session title from the first user message. */
|
|
7
9
|
function firstUserText(aiMessages) {
|
|
8
10
|
const u = aiMessages.find((m) => m?.role === "user");
|
|
@@ -40,11 +42,12 @@ export function buildChatRunInjection(execution) {
|
|
|
40
42
|
providerOptions: providerOpts,
|
|
41
43
|
maxTurns: MAX_TURNS,
|
|
42
44
|
toolChoice: modelToolChoice,
|
|
45
|
+
output: execution.modelOutput,
|
|
43
46
|
seedMessages: aiMessages,
|
|
44
|
-
toolSet: { ...toAITools(effectiveTools), ...(extraAiTools ?? {}), ...CLIENT_SIDE_TOOLS },
|
|
47
|
+
toolSet: { ...toAITools(effectiveTools), ...(extraAiTools ?? {}), ...(execution.clientSideTools ?? CLIENT_SIDE_TOOLS) },
|
|
45
48
|
activeToolNames: execution.activeToolNames,
|
|
46
49
|
executor: effectiveToolExecutor,
|
|
47
|
-
clientSideToolNames: CLIENT_SIDE_TOOL_NAMES,
|
|
50
|
+
clientSideToolNames: execution.clientSideToolNames ?? CLIENT_SIDE_TOOL_NAMES,
|
|
48
51
|
providerToolNames: new Set(Object.keys(extraAiTools ?? {})),
|
|
49
52
|
sandbox: execution.body.sandbox,
|
|
50
53
|
compactionTools: effectiveTools,
|
|
@@ -181,7 +184,7 @@ function newState() {
|
|
|
181
184
|
outputPolicyApplied: false,
|
|
182
185
|
};
|
|
183
186
|
}
|
|
184
|
-
async function applyStateOutputPolicy(execution, state, mode, signal) {
|
|
187
|
+
async function applyStateOutputPolicy(execution, state, mode, signal, validateStructured = true) {
|
|
185
188
|
if (state.outputPolicyApplied)
|
|
186
189
|
return;
|
|
187
190
|
state.finalText = await applyCompletionOutputPolicy({
|
|
@@ -193,6 +196,9 @@ async function applyStateOutputPolicy(execution, state, mode, signal) {
|
|
|
193
196
|
sessionId: execution.sessionId,
|
|
194
197
|
signal,
|
|
195
198
|
});
|
|
199
|
+
if (validateStructured) {
|
|
200
|
+
state.finalText = await finalizeResponseFormatText(execution.body.response_format, state.finalText);
|
|
201
|
+
}
|
|
196
202
|
state.outputPolicyApplied = true;
|
|
197
203
|
}
|
|
198
204
|
function captureRunFailure(state, outcome) {
|
|
@@ -238,9 +244,11 @@ export async function streamChatViaRun(c, execution) {
|
|
|
238
244
|
let assistantMsgId = null;
|
|
239
245
|
const state = newState();
|
|
240
246
|
const outputMode = streamingOutputPolicyMode(deps.runOutputPolicy);
|
|
247
|
+
const structuredResponse = isStructuredResponseFormat(body.response_format);
|
|
248
|
+
let suggestions = [];
|
|
241
249
|
let writeChain = Promise.resolve();
|
|
242
250
|
const write = (data) => { writeChain = writeChain.then(() => stream.writeSSE({ data })).catch(() => { }); };
|
|
243
|
-
const onEvent = makeOnEvent(execution, state, write, outputMode !== "buffer");
|
|
251
|
+
const onEvent = makeOnEvent(execution, state, write, outputMode !== "buffer" && !structuredResponse);
|
|
244
252
|
try {
|
|
245
253
|
await stream.writeSSE({ data: sseChunk(completionId, { role: "assistant" }) });
|
|
246
254
|
if (sessionStore && sessionId) {
|
|
@@ -256,10 +264,13 @@ export async function streamChatViaRun(c, execution) {
|
|
|
256
264
|
captureRunFailure(state, outcome);
|
|
257
265
|
await writeChain;
|
|
258
266
|
if (state.errorEvent && !abortController.signal.aborted) {
|
|
267
|
+
const structuredOutput = structuredOutputErrorEnvelope(state.errorEvent, structuredResponse);
|
|
259
268
|
const guardrail = guardrailErrorEnvelope(state.errorEvent);
|
|
260
269
|
const notFound = modelNotFoundEnvelope(state.errorEvent, m?.id, body.agent);
|
|
261
|
-
const error = guardrail ?? notFound ?? modelErrorEnvelope(state.errorEvent);
|
|
262
|
-
const text = guardrail || notFound
|
|
270
|
+
const error = structuredOutput ?? guardrail ?? notFound ?? modelErrorEnvelope(state.errorEvent);
|
|
271
|
+
const text = structuredOutput || guardrail || notFound
|
|
272
|
+
? ""
|
|
273
|
+
: `Model request failed: ${error.message}`;
|
|
263
274
|
if (text) {
|
|
264
275
|
state.finalText += text;
|
|
265
276
|
await stream.writeSSE({ data: sseChunk(completionId, { content: text }) });
|
|
@@ -268,8 +279,8 @@ export async function streamChatViaRun(c, execution) {
|
|
|
268
279
|
await stream.writeSSE({ data: "[DONE]" });
|
|
269
280
|
}
|
|
270
281
|
else if (state.clientReturn) {
|
|
271
|
-
await applyStateOutputPolicy(execution, state, outputMode === "buffer" ? "enforce" : "audit", abortController.signal);
|
|
272
|
-
if (outputMode === "buffer" && state.finalText) {
|
|
282
|
+
await applyStateOutputPolicy(execution, state, outputMode === "buffer" ? "enforce" : "audit", abortController.signal, false);
|
|
283
|
+
if ((outputMode === "buffer" || structuredResponse) && state.finalText) {
|
|
273
284
|
await stream.writeSSE({ data: sseChunk(completionId, { content: state.finalText }) });
|
|
274
285
|
}
|
|
275
286
|
state.toolCallsAccum.push({ id: state.clientReturn.id, name: state.clientReturn.name, arguments: state.clientReturn.arguments, state: "interrupted" });
|
|
@@ -278,18 +289,29 @@ export async function streamChatViaRun(c, execution) {
|
|
|
278
289
|
}
|
|
279
290
|
else if (!abortController.signal.aborted) {
|
|
280
291
|
await applyStateOutputPolicy(execution, state, outputMode === "buffer" ? "enforce" : "audit", abortController.signal);
|
|
281
|
-
if (outputMode === "buffer" && state.finalText) {
|
|
292
|
+
if ((outputMode === "buffer" || structuredResponse) && state.finalText) {
|
|
282
293
|
await stream.writeSSE({ data: sseChunk(completionId, { content: state.finalText }) });
|
|
283
294
|
}
|
|
295
|
+
suggestions = await suggestionsForCompletion(execution, state.finalText, abortController.signal);
|
|
296
|
+
if (suggestions.length > 0) {
|
|
297
|
+
await stream.writeSSE({
|
|
298
|
+
data: ssePolpoChunk(completionId, { suggestions }),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
284
301
|
await stream.writeSSE({ data: sseChunk(completionId, {}, "stop") });
|
|
285
302
|
await stream.writeSSE({ data: "[DONE]" });
|
|
286
303
|
}
|
|
287
304
|
}
|
|
288
305
|
catch (err) {
|
|
289
306
|
if (!((err instanceof DOMException && err.name === "AbortError") || abortController.signal.aborted)) {
|
|
307
|
+
const structuredOutput = structuredOutputErrorEnvelope(err, structuredResponse);
|
|
290
308
|
const guardrail = guardrailErrorEnvelope(err);
|
|
291
309
|
const notFound = modelNotFoundEnvelope(err, m?.id, body.agent);
|
|
292
|
-
if (
|
|
310
|
+
if (structuredOutput) {
|
|
311
|
+
await stream.writeSSE({ data: sseChunk(completionId, {}, "stop", { error: structuredOutput }) });
|
|
312
|
+
await stream.writeSSE({ data: "[DONE]" });
|
|
313
|
+
}
|
|
314
|
+
else if (guardrail) {
|
|
293
315
|
state.finalText = guardrail.message;
|
|
294
316
|
state.outputPolicyApplied = true;
|
|
295
317
|
await stream.writeSSE({ data: sseChunk(completionId, {}, "stop", { error: guardrail }) });
|
|
@@ -312,7 +334,7 @@ export async function streamChatViaRun(c, execution) {
|
|
|
312
334
|
finally {
|
|
313
335
|
clearInterval(heartbeat);
|
|
314
336
|
try {
|
|
315
|
-
await finishCommon(execution, state, assistantMsgId);
|
|
337
|
+
await finishCommon(execution, state, assistantMsgId, suggestions.length > 0 ? { suggestions } : undefined);
|
|
316
338
|
}
|
|
317
339
|
finally {
|
|
318
340
|
await steeringScope.release();
|
|
@@ -327,6 +349,7 @@ export async function runNonStreamingChatViaRun(c, execution) {
|
|
|
327
349
|
const steeringScope = await createRunSteeringScope(execution);
|
|
328
350
|
let assistantMsgId = null;
|
|
329
351
|
const state = newState();
|
|
352
|
+
let suggestions = [];
|
|
330
353
|
const onEvent = makeOnEvent(execution, state, () => { });
|
|
331
354
|
try {
|
|
332
355
|
if (sessionStore && sessionId) {
|
|
@@ -341,7 +364,7 @@ export async function runNonStreamingChatViaRun(c, execution) {
|
|
|
341
364
|
});
|
|
342
365
|
captureRunFailure(state, outcome);
|
|
343
366
|
if (!state.errorEvent) {
|
|
344
|
-
await applyStateOutputPolicy(execution, state, "enforce");
|
|
367
|
+
await applyStateOutputPolicy(execution, state, "enforce", undefined, !state.clientReturn);
|
|
345
368
|
}
|
|
346
369
|
if (state.clientReturn) {
|
|
347
370
|
return c.json({
|
|
@@ -357,6 +380,7 @@ export async function runNonStreamingChatViaRun(c, execution) {
|
|
|
357
380
|
});
|
|
358
381
|
}
|
|
359
382
|
if (state.errorEvent) {
|
|
383
|
+
const structuredOutput = structuredOutputErrorEnvelope(state.errorEvent, isStructuredResponseFormat(body.response_format));
|
|
360
384
|
const guardrail = guardrailErrorEnvelope(state.errorEvent);
|
|
361
385
|
if (guardrail) {
|
|
362
386
|
return c.json({ error: guardrail }, (guardrail.code === "guardrail_approval_required" ? 409 : 403));
|
|
@@ -364,13 +388,20 @@ export async function runNonStreamingChatViaRun(c, execution) {
|
|
|
364
388
|
const notFound = modelNotFoundEnvelope(state.errorEvent, m?.id, body.agent);
|
|
365
389
|
if (notFound)
|
|
366
390
|
return c.json({ error: notFound }, 400);
|
|
367
|
-
const error = modelErrorEnvelope(state.errorEvent);
|
|
368
|
-
|
|
391
|
+
const error = structuredOutput ?? modelErrorEnvelope(state.errorEvent);
|
|
392
|
+
if (!structuredOutput) {
|
|
393
|
+
state.finalText = state.finalText || `Model request failed: ${error.message}`;
|
|
394
|
+
}
|
|
369
395
|
return c.json({ error }, 400);
|
|
370
396
|
}
|
|
371
|
-
|
|
397
|
+
suggestions = await suggestionsForCompletion(execution, state.finalText, c.req.raw.signal);
|
|
398
|
+
return c.json(completionResponse(completionId, state.finalText, state.totalUsage, suggestions.length > 0 ? { polpo: { suggestions } } : undefined));
|
|
372
399
|
}
|
|
373
400
|
catch (err) {
|
|
401
|
+
const structuredOutput = structuredOutputErrorEnvelope(err, isStructuredResponseFormat(body.response_format));
|
|
402
|
+
if (structuredOutput) {
|
|
403
|
+
return c.json({ error: structuredOutput }, 400);
|
|
404
|
+
}
|
|
374
405
|
const guardrail = guardrailErrorEnvelope(err);
|
|
375
406
|
if (guardrail) {
|
|
376
407
|
state.finalText = guardrail.message;
|
|
@@ -386,7 +417,10 @@ export async function runNonStreamingChatViaRun(c, execution) {
|
|
|
386
417
|
}
|
|
387
418
|
finally {
|
|
388
419
|
try {
|
|
389
|
-
await finishCommon(execution, state, assistantMsgId, {
|
|
420
|
+
await finishCommon(execution, state, assistantMsgId, {
|
|
421
|
+
emptyFallback: "[Response interrupted]",
|
|
422
|
+
...(suggestions.length > 0 ? { suggestions } : {}),
|
|
423
|
+
});
|
|
390
424
|
}
|
|
391
425
|
finally {
|
|
392
426
|
await steeringScope.release();
|
|
@@ -454,10 +488,11 @@ export async function runChatTurnViaRun(execution, hooks = {}) {
|
|
|
454
488
|
}
|
|
455
489
|
let error;
|
|
456
490
|
if (state.errorEvent) {
|
|
491
|
+
const structuredOutput = structuredOutputErrorEnvelope(state.errorEvent, isStructuredResponseFormat(body.response_format));
|
|
457
492
|
const guardrail = guardrailErrorEnvelope(state.errorEvent);
|
|
458
493
|
const notFound = modelNotFoundEnvelope(state.errorEvent, m?.id, body.agent);
|
|
459
|
-
error = guardrail ?? notFound ?? modelErrorEnvelope(state.errorEvent);
|
|
460
|
-
if (!guardrail && !notFound && !state.finalText) {
|
|
494
|
+
error = structuredOutput ?? guardrail ?? notFound ?? modelErrorEnvelope(state.errorEvent);
|
|
495
|
+
if (!structuredOutput && !guardrail && !notFound && !state.finalText) {
|
|
461
496
|
state.finalText = `Model request failed: ${error.message}`;
|
|
462
497
|
}
|
|
463
498
|
}
|