@opengeni/sdk 3.3.2 → 3.4.0
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/README.md +1007 -72
- package/dist/adapters/express.d.ts +31 -0
- package/dist/adapters/express.js +87 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/hono.d.ts +16 -0
- package/dist/adapters/hono.js +8 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/next.d.ts +29 -0
- package/dist/adapters/next.js +22 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/allowance-refusal.d.ts +2 -0
- package/dist/allowance-refusal.js +50 -0
- package/dist/allowance-refusal.js.map +1 -0
- package/dist/app-links.d.ts +42 -0
- package/dist/app-links.js +66 -0
- package/dist/app-links.js.map +1 -0
- package/dist/artifact-catalog.d.ts +40 -0
- package/dist/artifact-client.d.ts +20 -8
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +8 -6
- package/dist/automations.js +8 -2
- package/dist/automations.js.map +1 -1
- package/dist/azure-live-transport.d.ts +32 -0
- package/dist/browser.d.ts +7 -2
- package/dist/browser.js +27 -5
- package/dist/chat/fold.d.ts +75 -0
- package/dist/chat/handler.d.ts +41 -0
- package/dist/chat/http.d.ts +67 -0
- package/dist/chat/ids.d.ts +34 -0
- package/dist/chat/index.d.ts +9 -0
- package/dist/chat/index.js +1369 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/openai.d.ts +30 -0
- package/dist/chat/opengeni.d.ts +129 -0
- package/dist/chat/types.d.ts +207 -0
- package/dist/chat/vercel.d.ts +49 -0
- package/dist/chats.d.ts +8 -0
- package/dist/chunk-4AHR4KGA.js +301 -0
- package/dist/chunk-4AHR4KGA.js.map +1 -0
- package/dist/chunk-6GYGMLX4.js +332 -0
- package/dist/chunk-6GYGMLX4.js.map +1 -0
- package/dist/chunk-7EWT3PV6.js +368 -0
- package/dist/chunk-7EWT3PV6.js.map +1 -0
- package/dist/chunk-FTWVVPR6.js +1665 -0
- package/dist/chunk-FTWVVPR6.js.map +1 -0
- package/dist/{chunk-VYCTL62C.js → chunk-GZQSWAKD.js} +1 -1
- package/dist/chunk-GZQSWAKD.js.map +1 -0
- package/dist/chunk-KGEABAID.js +71 -0
- package/dist/chunk-KGEABAID.js.map +1 -0
- package/dist/{chunk-YKZME56C.js → chunk-KWADQ227.js} +3 -3
- package/dist/{chunk-YKZME56C.js.map → chunk-KWADQ227.js.map} +1 -1
- package/dist/{chunk-JUS2UYRP.js → chunk-NCU5OAN7.js} +27 -2
- package/dist/chunk-NCU5OAN7.js.map +1 -0
- package/dist/{chunk-YXQX7RQT.js → chunk-OI5YLASH.js} +73 -2
- package/dist/chunk-OI5YLASH.js.map +1 -0
- package/dist/chunk-OV3POE62.js +359 -0
- package/dist/chunk-OV3POE62.js.map +1 -0
- package/dist/chunk-P7UWKORG.js +561 -0
- package/dist/chunk-P7UWKORG.js.map +1 -0
- package/dist/chunk-QGEIL4TR.js +195 -0
- package/dist/chunk-QGEIL4TR.js.map +1 -0
- package/dist/{chunk-TRNI7KEO.js → chunk-S2NLS3ES.js} +3669 -1281
- package/dist/chunk-S2NLS3ES.js.map +1 -0
- package/dist/chunk-UM4ZXXJR.js +9 -0
- package/dist/chunk-UM4ZXXJR.js.map +1 -0
- package/dist/{chunk-QT3KFVDP.js → chunk-UME3X7CP.js} +28 -32
- package/dist/chunk-UME3X7CP.js.map +1 -0
- package/dist/chunk-VT4YM4XQ.js +210 -0
- package/dist/chunk-VT4YM4XQ.js.map +1 -0
- package/dist/chunk-WSYYXONG.js +1602 -0
- package/dist/chunk-WSYYXONG.js.map +1 -0
- package/dist/{chunk-C2H7HBNP.js → chunk-WVCJRSGJ.js} +119 -14
- package/dist/chunk-WVCJRSGJ.js.map +1 -0
- package/dist/chunk-X5ITQVXE.js +15 -0
- package/dist/chunk-X5ITQVXE.js.map +1 -0
- package/dist/chunk-XA7HVIHI.js +126 -0
- package/dist/chunk-XA7HVIHI.js.map +1 -0
- package/dist/{chunk-KKRO2VCP.js → chunk-YJCHB7R3.js} +60 -12
- package/dist/chunk-YJCHB7R3.js.map +1 -0
- package/dist/chunk-YKIHNLWD.js +153 -0
- package/dist/chunk-YKIHNLWD.js.map +1 -0
- package/dist/chunk-ZDUBPV33.js +277 -0
- package/dist/chunk-ZDUBPV33.js.map +1 -0
- package/dist/client.d.ts +1068 -118
- package/dist/codex-realtime-controller.d.ts +13 -0
- package/dist/codex-realtime-controller.js +4 -2
- package/dist/codex-realtime-v3-wire.d.ts +3 -2
- package/dist/codex-realtime.d.ts +5 -1
- package/dist/company-brain.d.ts +1 -11
- package/dist/core.d.ts +7 -2
- package/dist/core.js +25 -6
- package/dist/deprecation.d.ts +47 -0
- package/dist/document-authority-client.d.ts +24 -2
- package/dist/document-authority.js +7 -5
- package/dist/editable-artifacts/index.d.ts +1 -1
- package/dist/editable-artifacts/types.d.ts +12 -2
- package/dist/editable-artifacts.js +19 -3
- package/dist/editable-artifacts.js.map +1 -1
- package/dist/embedding-client.d.ts +129 -0
- package/dist/errors.d.ts +76 -1
- package/dist/external-vault-receiver.d.ts +203 -0
- package/dist/external-vault-receiver.js +564 -0
- package/dist/external-vault-receiver.js.map +1 -0
- package/dist/feedback.d.ts +23 -0
- package/dist/gateway-realtime-transport.js +1 -1
- package/dist/github-repositories.d.ts +10 -0
- package/dist/github-repositories.js +36 -0
- package/dist/github-repositories.js.map +1 -0
- package/dist/index.d.ts +62 -11
- package/dist/index.js +557 -338
- package/dist/index.js.map +1 -1
- package/dist/insights-usage.d.ts +33 -0
- package/dist/insights-usage.js +7 -0
- package/dist/insights-usage.js.map +1 -0
- package/dist/interaction.d.ts +165 -71
- package/dist/interaction.js +2 -2
- package/dist/knowledge.d.ts +7 -0
- package/dist/knowledge.js +20 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/known-tools.d.ts +4 -0
- package/dist/message-links.d.ts +68 -0
- package/dist/model-context.d.ts +68 -0
- package/dist/model-display.d.ts +2 -0
- package/dist/model-display.js +17 -0
- package/dist/model-display.js.map +1 -0
- package/dist/model-picker-order.d.ts +1 -0
- package/dist/model-picker-order.js +10 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/organization-integration-policy.d.ts +26 -0
- package/dist/organization-integration-policy.js +26 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/pr-review-client.d.ts +1 -1
- package/dist/pr-review.d.ts +1 -1
- package/dist/pr-review.js.map +1 -1
- package/dist/preference-registry.d.ts +1 -1
- package/dist/prepared-mcp-setup.d.ts +5 -0
- package/dist/proxy.d.ts +13 -11
- package/dist/realtime.d.ts +1 -1
- package/dist/realtime.js +253 -7
- package/dist/realtime.js.map +1 -1
- package/dist/session-history-import.d.ts +22 -0
- package/dist/session-history-import.js +36 -0
- package/dist/session-history-import.js.map +1 -0
- package/dist/session-list-entries.d.ts +1 -0
- package/dist/session-list-entries.js +7 -0
- package/dist/session-list-entries.js.map +1 -0
- package/dist/session-message-search.d.ts +53 -0
- package/dist/session-proxy-client.d.ts +28 -0
- package/dist/session-proxy.d.ts +334 -0
- package/dist/session-proxy.js +24 -0
- package/dist/session-proxy.js.map +1 -0
- package/dist/session-titles.d.ts +2 -28
- package/dist/session-usage-limits.d.ts +10 -0
- package/dist/session-usage-limits.js +43 -0
- package/dist/session-usage-limits.js.map +1 -0
- package/dist/shared-catalog-loader.d.ts +34 -0
- package/dist/site-ai.d.ts +35 -0
- package/dist/site-bridge-host.d.ts +112 -0
- package/dist/site-bridge-host.js +286 -0
- package/dist/site-bridge-host.js.map +1 -0
- package/dist/site-browser-entry.d.ts +1 -0
- package/dist/site-browser-runtime.gen.d.ts +1 -0
- package/dist/site-data.d.ts +178 -0
- package/dist/site-document.d.ts +5 -0
- package/dist/site-document.js +20 -0
- package/dist/site-document.js.map +1 -0
- package/dist/site-document.test.d.ts +1 -0
- package/dist/site-http.d.ts +17 -0
- package/dist/site-sql.d.ts +31 -0
- package/dist/site-tool-bridge.d.ts +57 -0
- package/dist/site.d.ts +186 -0
- package/dist/site.js +63 -0
- package/dist/site.js.map +1 -0
- package/dist/skills.d.ts +75 -0
- package/dist/sse.d.ts +1 -1
- package/dist/stream.d.ts +2 -0
- package/dist/tenant-workspaces.d.ts +47 -0
- package/dist/tenant-workspaces.js +8 -0
- package/dist/tenant-workspaces.js.map +1 -0
- package/dist/tool-auth.d.ts +87 -0
- package/dist/tool-auth.js +23 -0
- package/dist/tool-auth.js.map +1 -0
- package/dist/tool-display-metadata.d.ts +3 -0
- package/dist/tools-37DCTCNZ.js +12 -0
- package/dist/tools-37DCTCNZ.js.map +1 -0
- package/dist/tools.d.ts +81 -0
- package/dist/transcription.d.ts +1 -1
- package/dist/types.d.ts +2807 -583
- package/dist/usage-allowances.d.ts +36 -0
- package/dist/usage-allowances.js +80 -0
- package/dist/usage-allowances.js.map +1 -0
- package/dist/vault-types.d.ts +324 -0
- package/dist/vaults-api.d.ts +65 -0
- package/dist/workspace-artifacts.d.ts +47 -5
- package/dist/workspace-instruction-policies.d.ts +2 -2
- package/dist/workspace-integrations.d.ts +74 -0
- package/dist/workspace-integrations.js +67 -0
- package/dist/workspace-integrations.js.map +1 -0
- package/dist/workspace-state.d.ts +8 -71
- package/package.json +103 -5
- package/src/adapters/express.ts +136 -0
- package/src/adapters/hono.ts +18 -0
- package/src/adapters/next.ts +42 -0
- package/src/allowance-refusal.ts +5 -0
- package/src/app-links.ts +117 -0
- package/src/artifact-catalog.ts +51 -0
- package/src/artifact-client.ts +40 -49
- package/src/artifacts.ts +1 -0
- package/src/automations-client.ts +8 -2
- package/src/azure-live-transport.ts +286 -0
- package/src/browser.ts +37 -0
- package/src/chat/fold.ts +431 -0
- package/src/chat/handler.ts +240 -0
- package/src/chat/http.ts +257 -0
- package/src/chat/ids.ts +85 -0
- package/src/chat/index.ts +88 -0
- package/src/chat/openai.ts +439 -0
- package/src/chat/opengeni.ts +737 -0
- package/src/chat/types.ts +219 -0
- package/src/chat/vercel.ts +204 -0
- package/src/chats.ts +16 -0
- package/src/client.ts +5697 -1833
- package/src/codex-realtime-controller.ts +163 -8
- package/src/codex-realtime-v3-wire.ts +4 -3
- package/src/codex-realtime-v3.ts +12 -3
- package/src/codex-realtime.ts +11 -3
- package/src/company-brain.ts +0 -40
- package/src/core.ts +20 -0
- package/src/deprecation.ts +149 -0
- package/src/document-authority-client.ts +104 -1
- package/src/editable-artifacts/controller.ts +36 -2
- package/src/editable-artifacts/index.ts +1 -0
- package/src/editable-artifacts/types.ts +29 -16
- package/src/embedding-client.ts +519 -0
- package/src/errors.ts +303 -4
- package/src/external-vault-receiver.ts +914 -0
- package/src/feedback.ts +20 -0
- package/src/gateway-realtime-transport.ts +3 -3
- package/src/github-repositories.ts +60 -0
- package/src/index.ts +603 -65
- package/src/insights-usage.ts +64 -0
- package/src/interaction.ts +315 -93
- package/src/knowledge.ts +72 -0
- package/src/known-tools.ts +279 -0
- package/src/message-links.ts +178 -0
- package/src/model-context.ts +113 -0
- package/src/model-display.ts +11 -0
- package/src/model-picker-order.ts +6 -0
- package/src/organization-integration-policy.ts +54 -0
- package/src/pr-review-client.ts +1 -1
- package/src/pr-review.ts +1 -1
- package/src/preference-registry.ts +2 -0
- package/src/prepared-mcp-setup.ts +123 -0
- package/src/proxy.ts +23 -12
- package/src/realtime.ts +15 -6
- package/src/session-history-import.ts +84 -0
- package/src/session-list-entries.ts +1 -0
- package/src/session-message-search.ts +47 -0
- package/src/session-proxy-client.ts +68 -0
- package/src/session-proxy.ts +2098 -0
- package/src/session-titles.ts +8 -82
- package/src/session-usage-limits.ts +71 -0
- package/src/shared-catalog-loader.ts +169 -0
- package/src/site-ai.ts +284 -0
- package/src/site-bridge-host.ts +422 -0
- package/src/site-browser-entry.ts +6 -0
- package/src/site-browser-runtime.gen.ts +2 -0
- package/src/site-data.ts +372 -0
- package/src/site-document.test.ts +64 -0
- package/src/site-document.ts +18 -0
- package/src/site-http.ts +171 -0
- package/src/site-sql.ts +153 -0
- package/src/site-tool-bridge.ts +401 -0
- package/src/site.ts +878 -0
- package/src/skills.ts +69 -0
- package/src/sse.ts +9 -3
- package/src/stream.ts +33 -10
- package/src/tenant-workspaces.ts +148 -0
- package/src/tool-auth.ts +301 -0
- package/src/tool-display-metadata.ts +27 -0
- package/src/tools.ts +452 -0
- package/src/transcription.ts +3 -0
- package/src/types.ts +3170 -582
- package/src/usage-allowances.ts +189 -0
- package/src/vault-types.ts +379 -0
- package/src/vaults-api.ts +339 -0
- package/src/workspace-artifacts.ts +55 -6
- package/src/workspace-instruction-policies.ts +2 -1
- package/src/workspace-integrations.ts +402 -0
- package/src/workspace-state.ts +8 -82
- package/dist/chunk-C2H7HBNP.js.map +0 -1
- package/dist/chunk-C4DJPZRU.js +0 -119
- package/dist/chunk-C4DJPZRU.js.map +0 -1
- package/dist/chunk-JUS2UYRP.js.map +0 -1
- package/dist/chunk-KKRO2VCP.js.map +0 -1
- package/dist/chunk-QT3KFVDP.js.map +0 -1
- package/dist/chunk-TRNI7KEO.js.map +0 -1
- package/dist/chunk-VYCTL62C.js.map +0 -1
- package/dist/chunk-YXQX7RQT.js.map +0 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chatErrorSummary,
|
|
3
|
+
clientConversation,
|
|
4
|
+
errorResponse,
|
|
5
|
+
importedHistoryBefore,
|
|
6
|
+
jsonResponse,
|
|
7
|
+
lastUserMessageText,
|
|
8
|
+
messageContentText,
|
|
9
|
+
openResolvedChat,
|
|
10
|
+
readJsonObject,
|
|
11
|
+
resolveChatRequest,
|
|
12
|
+
sseByteStream,
|
|
13
|
+
sseHeaders,
|
|
14
|
+
sseLine,
|
|
15
|
+
type ChatResolve,
|
|
16
|
+
} from "./http";
|
|
17
|
+
import { isUuid } from "./ids";
|
|
18
|
+
import type { Chat, Opengeni } from "./opengeni";
|
|
19
|
+
import { OpenGeniChatError, type ChatChunk, type ChatReply } from "./types";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* OpenAI-compatible adapters over the stable subset of the Chat Completions
|
|
23
|
+
* and Responses wire formats. `model` and `user` in the body are echoed, never
|
|
24
|
+
* trusted: identity comes from the host's `resolve` hook.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const DEFAULT_MODEL_LABEL = "opengeni";
|
|
28
|
+
|
|
29
|
+
type ReplyExtension = {
|
|
30
|
+
sessionId: string;
|
|
31
|
+
workspaceId: string;
|
|
32
|
+
turnId: string | null;
|
|
33
|
+
status: ChatReply["status"];
|
|
34
|
+
pending: ChatReply["pending"];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function replyExtension(reply: ChatReply): ReplyExtension {
|
|
38
|
+
return {
|
|
39
|
+
sessionId: reply.sessionId,
|
|
40
|
+
workspaceId: reply.workspaceId,
|
|
41
|
+
turnId: reply.turnId,
|
|
42
|
+
status: reply.status,
|
|
43
|
+
pending: reply.pending,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function conversationFromBody(body: Record<string, unknown> | null): string | undefined {
|
|
48
|
+
const conversation = body?.conversation;
|
|
49
|
+
if (typeof conversation === "string" && conversation) return conversation;
|
|
50
|
+
if (conversation && typeof conversation === "object") {
|
|
51
|
+
const id = (conversation as { id?: unknown }).id;
|
|
52
|
+
if (typeof id === "string" && id) return id;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function modelLabel(body: Record<string, unknown> | null): string {
|
|
58
|
+
return typeof body?.model === "string" && body.model ? body.model : DEFAULT_MODEL_LABEL;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function wantsStream(body: Record<string, unknown> | null): boolean {
|
|
62
|
+
return body?.stream === true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// --- Chat Completions ---------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
const CHAT_COMPLETION_PART_TYPES = ["text", "input_text"];
|
|
68
|
+
|
|
69
|
+
function chatCompletionChunk(
|
|
70
|
+
sessionId: string,
|
|
71
|
+
model: string,
|
|
72
|
+
created: number,
|
|
73
|
+
delta: Record<string, unknown>,
|
|
74
|
+
finishReason: "stop" | null,
|
|
75
|
+
extension?: ReplyExtension,
|
|
76
|
+
): string {
|
|
77
|
+
return sseLine(
|
|
78
|
+
JSON.stringify({
|
|
79
|
+
id: `chatcmpl-${sessionId}`,
|
|
80
|
+
object: "chat.completion.chunk",
|
|
81
|
+
created,
|
|
82
|
+
model,
|
|
83
|
+
choices: [{ index: 0, delta, finish_reason: finishReason }],
|
|
84
|
+
...(extension ? { opengeni: extension } : {}),
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function* chatCompletionBlocks(
|
|
90
|
+
chat: Chat,
|
|
91
|
+
chunks: AsyncIterable<ChatChunk>,
|
|
92
|
+
model: string,
|
|
93
|
+
): AsyncGenerator<string, void, void> {
|
|
94
|
+
const created = Math.floor(Date.now() / 1000);
|
|
95
|
+
yield chatCompletionChunk(
|
|
96
|
+
chat.sessionId,
|
|
97
|
+
model,
|
|
98
|
+
created,
|
|
99
|
+
{ role: "assistant", content: "" },
|
|
100
|
+
null,
|
|
101
|
+
);
|
|
102
|
+
try {
|
|
103
|
+
for await (const chunk of chunks) {
|
|
104
|
+
if (chunk.type === "text") {
|
|
105
|
+
yield chatCompletionChunk(chat.sessionId, model, created, { content: chunk.text }, null);
|
|
106
|
+
} else if (chunk.type === "done") {
|
|
107
|
+
yield chatCompletionChunk(
|
|
108
|
+
chat.sessionId,
|
|
109
|
+
model,
|
|
110
|
+
created,
|
|
111
|
+
{},
|
|
112
|
+
"stop",
|
|
113
|
+
replyExtension(chunk.reply),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
const summary = chatErrorSummary(error);
|
|
119
|
+
yield sseLine(
|
|
120
|
+
JSON.stringify({
|
|
121
|
+
error: { message: summary.message, code: summary.code, type: "server_error" },
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
yield sseLine("[DONE]");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function chatCompletionObject(reply: ChatReply, model: string): Record<string, unknown> {
|
|
129
|
+
return {
|
|
130
|
+
id: `chatcmpl-${reply.sessionId}`,
|
|
131
|
+
object: "chat.completion",
|
|
132
|
+
created: Math.floor(Date.now() / 1000),
|
|
133
|
+
model,
|
|
134
|
+
choices: [
|
|
135
|
+
{ index: 0, message: { role: "assistant", content: reply.text }, finish_reason: "stop" },
|
|
136
|
+
],
|
|
137
|
+
opengeni: replyExtension(reply),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* `POST /v1/chat/completions` shape: `{ messages, stream?, model?, user?, metadata? }`.
|
|
143
|
+
* Conversation: the host's `resolve`, else the `x-opengeni-conversation` header,
|
|
144
|
+
* else `metadata.conversation_id`; 400 when none. Only the last user message
|
|
145
|
+
* is sent; the messages before it are imported once as context when this
|
|
146
|
+
* request creates the session.
|
|
147
|
+
*/
|
|
148
|
+
export async function handleChatCompletionsRequest(
|
|
149
|
+
og: Opengeni,
|
|
150
|
+
request: Request,
|
|
151
|
+
resolve: ChatResolve,
|
|
152
|
+
): Promise<Response> {
|
|
153
|
+
const body = await readJsonObject(request);
|
|
154
|
+
const prompt =
|
|
155
|
+
lastUserMessageText(body?.messages, CHAT_COMPLETION_PART_TYPES, "content")?.trim() ?? "";
|
|
156
|
+
if (!prompt) {
|
|
157
|
+
return errorResponse(400, "The last user message has no text.", "message_required");
|
|
158
|
+
}
|
|
159
|
+
const resolved = await resolveChatRequest(request, resolve);
|
|
160
|
+
if (resolved.response) return resolved.response;
|
|
161
|
+
const metadata = body?.metadata;
|
|
162
|
+
const metadataConversation =
|
|
163
|
+
metadata && typeof metadata === "object"
|
|
164
|
+
? (metadata as { conversation_id?: unknown }).conversation_id
|
|
165
|
+
: undefined;
|
|
166
|
+
const opened = await openResolvedChat(
|
|
167
|
+
og,
|
|
168
|
+
resolved.resolution,
|
|
169
|
+
clientConversation(request, metadataConversation),
|
|
170
|
+
);
|
|
171
|
+
if (opened.response) return opened.response;
|
|
172
|
+
const model = modelLabel(body);
|
|
173
|
+
const importedHistory = importedHistoryBefore(
|
|
174
|
+
body?.messages,
|
|
175
|
+
CHAT_COMPLETION_PART_TYPES,
|
|
176
|
+
"content",
|
|
177
|
+
);
|
|
178
|
+
if (wantsStream(body)) {
|
|
179
|
+
const chunks = opened.chat.stream(prompt, { signal: request.signal, importedHistory });
|
|
180
|
+
return new Response(sseByteStream(chatCompletionBlocks(opened.chat, chunks, model)), {
|
|
181
|
+
headers: sseHeaders(),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const reply = await opened.chat.send(prompt, { signal: request.signal, importedHistory });
|
|
186
|
+
return jsonResponse(chatCompletionObject(reply, model));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const summary = chatErrorSummary(error);
|
|
189
|
+
return errorResponse(summary.status, summary.message, summary.code);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- Responses ----------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
/** Non-streaming response id; the session id alone addresses the conversation. */
|
|
196
|
+
export function encodeResponseId(sessionId: string, sequence: number): string {
|
|
197
|
+
return `resp_${sessionId}_${sequence}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function decodeResponseId(value: unknown): string | null {
|
|
201
|
+
if (typeof value !== "string") return null;
|
|
202
|
+
// Streams allocate a UUID before the final event sequence is known. Keep
|
|
203
|
+
// accepting existing sequence-based response ids for conversation continuity.
|
|
204
|
+
const match = /^resp_([0-9a-f-]{36})(?:_(\d+|[0-9a-f-]{36}))?$/i.exec(value);
|
|
205
|
+
const sessionId = match?.[1];
|
|
206
|
+
const suffix = match?.[2];
|
|
207
|
+
if (suffix && !/^\d+$/.test(suffix) && !isUuid(suffix)) return null;
|
|
208
|
+
return sessionId && isUuid(sessionId) ? sessionId : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
type ResponseIdentity = { responseId: string; itemId: string; createdAt: number };
|
|
212
|
+
type ResponseStatus = "in_progress" | "completed" | "incomplete";
|
|
213
|
+
|
|
214
|
+
function responseOutputItem(itemId: string, status: ResponseStatus, text: string | null) {
|
|
215
|
+
return {
|
|
216
|
+
type: "message",
|
|
217
|
+
id: itemId,
|
|
218
|
+
status,
|
|
219
|
+
role: "assistant",
|
|
220
|
+
content: text === null ? [] : [{ type: "output_text", text, annotations: [] }],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function responseObject(
|
|
225
|
+
reply: ChatReply | null,
|
|
226
|
+
chat: Chat,
|
|
227
|
+
model: string,
|
|
228
|
+
status: ResponseStatus,
|
|
229
|
+
previousResponseId: string | null,
|
|
230
|
+
text: string,
|
|
231
|
+
identity?: ResponseIdentity,
|
|
232
|
+
): Record<string, unknown> {
|
|
233
|
+
const sequence = reply?.events.at(-1)?.sequence ?? 0;
|
|
234
|
+
return {
|
|
235
|
+
id: identity?.responseId ?? encodeResponseId(chat.sessionId, sequence),
|
|
236
|
+
object: "response",
|
|
237
|
+
created_at: identity?.createdAt ?? Math.floor(Date.now() / 1000),
|
|
238
|
+
status,
|
|
239
|
+
model,
|
|
240
|
+
previous_response_id: previousResponseId,
|
|
241
|
+
output:
|
|
242
|
+
status === "in_progress"
|
|
243
|
+
? []
|
|
244
|
+
: [
|
|
245
|
+
responseOutputItem(
|
|
246
|
+
identity?.itemId ?? `msg_${chat.sessionId}_${sequence}`,
|
|
247
|
+
status,
|
|
248
|
+
text,
|
|
249
|
+
),
|
|
250
|
+
],
|
|
251
|
+
output_text: text,
|
|
252
|
+
...(reply ? { opengeni: replyExtension(reply) } : {}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function* responsesBlocks(
|
|
257
|
+
chat: Chat,
|
|
258
|
+
chunks: AsyncIterable<ChatChunk>,
|
|
259
|
+
model: string,
|
|
260
|
+
previousResponseId: string | null,
|
|
261
|
+
): AsyncGenerator<string, void, void> {
|
|
262
|
+
let sequenceNumber = 0;
|
|
263
|
+
const requestId = crypto.randomUUID();
|
|
264
|
+
const identity: ResponseIdentity = {
|
|
265
|
+
responseId: `resp_${chat.sessionId}_${requestId}`,
|
|
266
|
+
itemId: `msg_${chat.sessionId}_${requestId}`,
|
|
267
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
268
|
+
};
|
|
269
|
+
const { itemId } = identity;
|
|
270
|
+
const event = (type: string, data: Record<string, unknown>): string =>
|
|
271
|
+
sseLine(JSON.stringify({ type, sequence_number: sequenceNumber++, ...data }), type);
|
|
272
|
+
const initial = responseObject(
|
|
273
|
+
null,
|
|
274
|
+
chat,
|
|
275
|
+
model,
|
|
276
|
+
"in_progress",
|
|
277
|
+
previousResponseId,
|
|
278
|
+
"",
|
|
279
|
+
identity,
|
|
280
|
+
);
|
|
281
|
+
yield event("response.created", { response: initial });
|
|
282
|
+
yield event("response.in_progress", { response: initial });
|
|
283
|
+
yield event("response.output_item.added", {
|
|
284
|
+
output_index: 0,
|
|
285
|
+
item: responseOutputItem(itemId, "in_progress", null),
|
|
286
|
+
});
|
|
287
|
+
yield event("response.content_part.added", {
|
|
288
|
+
item_id: itemId,
|
|
289
|
+
output_index: 0,
|
|
290
|
+
content_index: 0,
|
|
291
|
+
part: { type: "output_text", text: "", annotations: [] },
|
|
292
|
+
});
|
|
293
|
+
try {
|
|
294
|
+
for await (const chunk of chunks) {
|
|
295
|
+
if (chunk.type === "text") {
|
|
296
|
+
yield event("response.output_text.delta", {
|
|
297
|
+
item_id: itemId,
|
|
298
|
+
output_index: 0,
|
|
299
|
+
content_index: 0,
|
|
300
|
+
delta: chunk.text,
|
|
301
|
+
logprobs: [],
|
|
302
|
+
});
|
|
303
|
+
} else if (chunk.type === "done") {
|
|
304
|
+
const status = chunk.reply.status === "completed" ? "completed" : "incomplete";
|
|
305
|
+
const item = responseOutputItem(itemId, status, chunk.reply.text);
|
|
306
|
+
yield event("response.output_text.done", {
|
|
307
|
+
item_id: itemId,
|
|
308
|
+
output_index: 0,
|
|
309
|
+
content_index: 0,
|
|
310
|
+
text: chunk.reply.text,
|
|
311
|
+
logprobs: [],
|
|
312
|
+
});
|
|
313
|
+
yield event("response.content_part.done", {
|
|
314
|
+
item_id: itemId,
|
|
315
|
+
output_index: 0,
|
|
316
|
+
content_index: 0,
|
|
317
|
+
part: item.content[0]!,
|
|
318
|
+
});
|
|
319
|
+
yield event("response.output_item.done", { output_index: 0, item });
|
|
320
|
+
yield event(`response.${status}`, {
|
|
321
|
+
response: responseObject(
|
|
322
|
+
chunk.reply,
|
|
323
|
+
chat,
|
|
324
|
+
model,
|
|
325
|
+
status,
|
|
326
|
+
previousResponseId,
|
|
327
|
+
chunk.reply.text,
|
|
328
|
+
identity,
|
|
329
|
+
),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
const summary = chatErrorSummary(error);
|
|
335
|
+
yield event("error", { code: summary.code, message: summary.message, param: null });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Text of the Responses `input`: a string, or the last user item's `input_text` parts. */
|
|
340
|
+
export function responsesInputText(input: unknown): string | null {
|
|
341
|
+
if (typeof input === "string") return input;
|
|
342
|
+
if (!Array.isArray(input)) return null;
|
|
343
|
+
const fromRole = lastUserMessageText(input, ["input_text", "text"], "content");
|
|
344
|
+
if (fromRole !== null) return fromRole;
|
|
345
|
+
const last = input.at(-1);
|
|
346
|
+
return last && typeof last === "object"
|
|
347
|
+
? messageContentText((last as { content?: unknown }).content, ["input_text", "text"])
|
|
348
|
+
: null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* `POST /v1/responses` shape: `{ input, previous_response_id?, conversation?, stream?, model?, user? }`.
|
|
353
|
+
* Conversation: the host's `resolve`, else the `x-opengeni-conversation`
|
|
354
|
+
* header, else `conversation` / `conversation.id`. A `previous_response_id`
|
|
355
|
+
* may continue the session it encodes only when the host named the user that
|
|
356
|
+
* session belongs to (403 otherwise); when a conversation is named as well,
|
|
357
|
+
* the two must agree (409). 400 when nothing names a conversation. Only the
|
|
358
|
+
* last user item is sent; earlier array items are imported once as context
|
|
359
|
+
* when this request creates the session.
|
|
360
|
+
*/
|
|
361
|
+
export async function handleResponsesRequest(
|
|
362
|
+
og: Opengeni,
|
|
363
|
+
request: Request,
|
|
364
|
+
resolve: ChatResolve,
|
|
365
|
+
): Promise<Response> {
|
|
366
|
+
const body = await readJsonObject(request);
|
|
367
|
+
const prompt = responsesInputText(body?.input)?.trim() ?? "";
|
|
368
|
+
if (!prompt) return errorResponse(400, "The input has no user text.", "message_required");
|
|
369
|
+
const resolved = await resolveChatRequest(request, resolve);
|
|
370
|
+
if (resolved.response) return resolved.response;
|
|
371
|
+
const previousResponseId =
|
|
372
|
+
typeof body?.previous_response_id === "string" ? body.previous_response_id : null;
|
|
373
|
+
const conversation = clientConversation(request, conversationFromBody(body));
|
|
374
|
+
const previousSessionId = decodeResponseId(previousResponseId);
|
|
375
|
+
const namedConversation =
|
|
376
|
+
resolved.resolution.conversation ?? (resolved.resolution.user ? conversation : undefined);
|
|
377
|
+
let chat: Chat;
|
|
378
|
+
if (previousSessionId && !namedConversation) {
|
|
379
|
+
if (!resolved.resolution.user) {
|
|
380
|
+
return errorResponse(
|
|
381
|
+
400,
|
|
382
|
+
"Return a conversation from resolve, or a user so client conversation ids are scoped to that user.",
|
|
383
|
+
"conversation_required",
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
const workspaceId = await og.workspaceId(resolved.resolution);
|
|
388
|
+
chat = await og.chatBySessionId({
|
|
389
|
+
workspaceId,
|
|
390
|
+
sessionId: previousSessionId,
|
|
391
|
+
user: resolved.resolution.user,
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
const summary = chatErrorSummary(error);
|
|
395
|
+
return errorResponse(summary.status, summary.message, summary.code);
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
const opened = await openResolvedChat(og, resolved.resolution, conversation);
|
|
399
|
+
if (opened.response) return opened.response;
|
|
400
|
+
chat = opened.chat;
|
|
401
|
+
if (previousSessionId && previousSessionId !== chat.sessionId) {
|
|
402
|
+
const summary = chatErrorSummary(
|
|
403
|
+
new OpenGeniChatError(
|
|
404
|
+
"conversation_mismatch",
|
|
405
|
+
"previous_response_id belongs to a different conversation than the one named for this request.",
|
|
406
|
+
),
|
|
407
|
+
);
|
|
408
|
+
return errorResponse(summary.status, summary.message, summary.code);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const model = modelLabel(body);
|
|
412
|
+
const importedHistory = importedHistoryBefore(
|
|
413
|
+
body?.input,
|
|
414
|
+
["input_text", "text", "output_text"],
|
|
415
|
+
"content",
|
|
416
|
+
);
|
|
417
|
+
if (wantsStream(body)) {
|
|
418
|
+
const chunks = chat.stream(prompt, { signal: request.signal, importedHistory });
|
|
419
|
+
return new Response(sseByteStream(responsesBlocks(chat, chunks, model, previousResponseId)), {
|
|
420
|
+
headers: sseHeaders(),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
const reply = await chat.send(prompt, { signal: request.signal, importedHistory });
|
|
425
|
+
return jsonResponse(
|
|
426
|
+
responseObject(
|
|
427
|
+
reply,
|
|
428
|
+
chat,
|
|
429
|
+
model,
|
|
430
|
+
reply.status === "completed" ? "completed" : "incomplete",
|
|
431
|
+
previousResponseId,
|
|
432
|
+
reply.text,
|
|
433
|
+
),
|
|
434
|
+
);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
const summary = chatErrorSummary(error);
|
|
437
|
+
return errorResponse(summary.status, summary.message, summary.code);
|
|
438
|
+
}
|
|
439
|
+
}
|