@ryanfw/prompt-orchestration-pipeline 1.3.2 → 1.3.4
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/docs/http-api.md +80 -4
- package/package.json +1 -4
- package/src/api/__tests__/index.test.ts +443 -32
- package/src/api/index.ts +108 -127
- package/src/cli/__tests__/analyze-task.test.ts +40 -7
- package/src/cli/__tests__/index.test.ts +337 -17
- package/src/cli/__tests__/types.test.ts +0 -18
- package/src/cli/__tests__/update-pipeline-json.test.ts +19 -9
- package/src/cli/analyze-task.ts +3 -14
- package/src/cli/index.ts +73 -61
- package/src/cli/types.ts +0 -13
- package/src/cli/update-pipeline-json.ts +3 -14
- package/src/config/__tests__/paths.test.ts +46 -1
- package/src/config/__tests__/pipeline-registry.test.ts +767 -0
- package/src/config/__tests__/sse-events.test.ts +470 -0
- package/src/config/__tests__/statuses.test.ts +41 -0
- package/src/config/paths.ts +11 -2
- package/src/config/pipeline-registry.ts +258 -0
- package/src/config/sse-events.ts +122 -0
- package/src/config/statuses.ts +20 -1
- package/src/core/__tests__/agent-step.test.ts +115 -0
- package/src/core/__tests__/config.test.ts +545 -105
- package/src/core/__tests__/core-boot-resolution.test.ts +181 -0
- package/src/core/__tests__/job-concurrency.test.ts +260 -0
- package/src/core/__tests__/job-submission.test.ts +396 -0
- package/src/core/__tests__/job-view.test.ts +1341 -0
- package/src/core/__tests__/json-file.test.ts +111 -0
- package/src/core/__tests__/lifecycle-policy.test.ts +81 -7
- package/src/core/__tests__/logger.test.ts +22 -0
- package/src/core/__tests__/orchestrator-no-deprecated-exports.test.ts +41 -0
- package/src/core/__tests__/orchestrator.test.ts +615 -2
- package/src/core/__tests__/pipeline-runner.test.ts +946 -9
- package/src/core/__tests__/redact.test.ts +153 -0
- package/src/core/__tests__/runner-liveness.test.ts +910 -0
- package/src/core/__tests__/seed-naming.test.ts +57 -0
- package/src/core/__tests__/single-derivation.test.ts +159 -0
- package/src/core/__tests__/task-runner.test.ts +594 -3
- package/src/core/__tests__/task-telemetry.test.ts +241 -0
- package/src/core/__tests__/workspace-root-resolution-guard.test.ts +62 -0
- package/src/core/agent-step.ts +4 -1
- package/src/core/agent-types.ts +5 -4
- package/src/core/config.ts +134 -222
- package/src/core/control.ts +3 -0
- package/src/core/job-concurrency.ts +56 -20
- package/src/core/job-submission.ts +133 -0
- package/src/core/job-view.ts +473 -0
- package/src/core/json-file.ts +45 -0
- package/src/core/lifecycle-policy.ts +23 -8
- package/src/core/logger.ts +0 -29
- package/src/core/orchestrator.ts +200 -74
- package/src/core/pipeline-runner.ts +85 -53
- package/src/core/redact.ts +40 -0
- package/src/core/runner-liveness.ts +280 -0
- package/src/core/seed-naming.ts +9 -0
- package/src/core/status-writer.ts +27 -33
- package/src/core/task-runner.ts +356 -319
- package/src/core/task-telemetry.ts +107 -0
- package/src/harness/__tests__/subprocess.test.ts +112 -1
- package/src/harness/subprocess.ts +55 -16
- package/src/llm/__tests__/index.test.ts +684 -33
- package/src/llm/index.ts +310 -67
- package/src/providers/__tests__/alibaba.test.ts +67 -6
- package/src/providers/__tests__/anthropic.test.ts +35 -14
- package/src/providers/__tests__/base.test.ts +62 -0
- package/src/providers/__tests__/claude-code.test.ts +99 -14
- package/src/providers/__tests__/deepseek.test.ts +16 -6
- package/src/providers/__tests__/gemini.test.ts +47 -25
- package/src/providers/__tests__/moonshot.test.ts +27 -0
- package/src/providers/__tests__/openai.test.ts +262 -74
- package/src/providers/__tests__/opencode.test.ts +77 -0
- package/src/providers/__tests__/request-timeout-contract.test.ts +226 -0
- package/src/providers/__tests__/stream-accumulator.test.ts +52 -0
- package/src/providers/__tests__/types.test.ts +85 -0
- package/src/providers/__tests__/zero-fill-guard.test.ts +62 -0
- package/src/providers/__tests__/zhipu.test.ts +27 -14
- package/src/providers/alibaba.ts +20 -39
- package/src/providers/anthropic.ts +23 -11
- package/src/providers/base.ts +19 -0
- package/src/providers/claude-code.ts +27 -18
- package/src/providers/deepseek.ts +9 -28
- package/src/providers/gemini.ts +20 -58
- package/src/providers/moonshot.ts +15 -11
- package/src/providers/openai.ts +79 -61
- package/src/providers/opencode.ts +16 -33
- package/src/providers/stream-accumulator.ts +27 -21
- package/src/providers/types.ts +29 -4
- package/src/providers/zhipu.ts +15 -44
- package/src/task-analysis/__tests__/analyzer.test.ts +0 -0
- package/src/task-analysis/__tests__/enrichers-schema-deducer.test.ts +34 -2
- package/src/task-analysis/__tests__/no-ast-imports.test.ts +52 -0
- package/src/task-analysis/__tests__/repository.test.ts +65 -0
- package/src/task-analysis/__tests__/types.test.ts +63 -130
- package/src/task-analysis/analyzer.ts +91 -0
- package/src/task-analysis/enrichers/schema-deducer.ts +13 -2
- package/src/task-analysis/index.ts +2 -36
- package/src/task-analysis/repository.ts +45 -0
- package/src/task-analysis/types.ts +42 -58
- package/src/ui/client/__tests__/api.test.ts +143 -1
- package/src/ui/client/__tests__/bootstrap.test.ts +178 -62
- package/src/ui/client/__tests__/job-adapter.test.ts +203 -2
- package/src/ui/client/__tests__/load-state.test.ts +70 -0
- package/src/ui/client/__tests__/types.test.ts +66 -3
- package/src/ui/client/__tests__/useJobDetailWithUpdates.test.ts +390 -77
- package/src/ui/client/__tests__/useJobList.test.ts +198 -23
- package/src/ui/client/__tests__/useJobListWithUpdates.test.ts +218 -7
- package/src/ui/client/adapters/__tests__/job-adapter.test.ts +186 -0
- package/src/ui/client/adapters/job-adapter.ts +41 -16
- package/src/ui/client/api.ts +38 -15
- package/src/ui/client/bootstrap.ts +19 -14
- package/src/ui/client/hooks/useJobDetailWithUpdates.ts +73 -97
- package/src/ui/client/hooks/useJobList.ts +26 -31
- package/src/ui/client/hooks/useJobListWithUpdates.ts +42 -76
- package/src/ui/client/load-state.ts +20 -0
- package/src/ui/client/reducers/__tests__/job-events.test.ts +568 -0
- package/src/ui/client/reducers/job-events.ts +137 -0
- package/src/ui/client/types.ts +16 -20
- package/src/ui/components/DAGGrid.tsx +6 -1
- package/src/ui/components/JobDetail.tsx +12 -4
- package/src/ui/components/JobTable.tsx +41 -13
- package/src/ui/components/StageTimeline.tsx +8 -20
- package/src/ui/components/TaskAnalysisDisplay.tsx +48 -6
- package/src/ui/components/__tests__/DAGGrid.test.tsx +36 -0
- package/src/ui/components/__tests__/JobDetail.test.tsx +112 -0
- package/src/ui/components/__tests__/JobTable.test.tsx +137 -1
- package/src/ui/components/__tests__/StageTimeline.test.tsx +5 -6
- package/src/ui/components/__tests__/TaskAnalysisDisplay.test.tsx +64 -0
- package/src/ui/components/types.ts +35 -15
- package/src/ui/dist/assets/{index--RH3sAt3.js → index-DN3-zvtP.js} +4324 -263
- package/src/ui/dist/assets/index-DN3-zvtP.js.map +1 -0
- package/src/ui/dist/assets/style-CtZBnjlR.css +2 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/pages/PipelineDetail.tsx +60 -4
- package/src/ui/pages/PromptPipelineDashboard.tsx +59 -7
- package/src/ui/pages/__tests__/PromptPipelineDashboard.test.tsx +114 -32
- package/src/ui/pages/__tests__/pages.test.tsx +236 -42
- package/src/ui/server/__tests__/concurrency-endpoint.test.ts +54 -0
- package/src/ui/server/__tests__/config-bridge-node.test.ts +34 -1
- package/src/ui/server/__tests__/config-bridge.test.ts +9 -1
- package/src/ui/server/__tests__/embedded-assets.test.ts +66 -0
- package/src/ui/server/__tests__/file-endpoints.test.ts +183 -5
- package/src/ui/server/__tests__/gate-endpoints.test.ts +55 -0
- package/src/ui/server/__tests__/index.test.ts +63 -0
- package/src/ui/server/__tests__/job-control-endpoints.test.ts +512 -2
- package/src/ui/server/__tests__/job-endpoints.test.ts +158 -2
- package/src/ui/server/__tests__/read-static-path-guard.test.ts +94 -0
- package/src/ui/server/__tests__/router-root-isolation.test.ts +204 -0
- package/src/ui/server/__tests__/router-threading.test.ts +148 -0
- package/src/ui/server/__tests__/router.test.ts +104 -0
- package/src/ui/server/__tests__/sse-broadcast.test.ts +44 -0
- package/src/ui/server/__tests__/sse-enhancer.test.ts +70 -0
- package/src/ui/server/config-bridge-node.ts +8 -26
- package/src/ui/server/config-bridge.ts +3 -4
- package/src/ui/server/embedded-assets-imports.d.ts +44 -0
- package/src/ui/server/embedded-assets.ts +13 -2
- package/src/ui/server/endpoints/__tests__/meta-endpoint.test.ts +1 -0
- package/src/ui/server/endpoints/__tests__/pipeline-analysis-endpoint.test.ts +167 -0
- package/src/ui/server/endpoints/__tests__/schema-file-endpoint.test.ts +104 -0
- package/src/ui/server/endpoints/__tests__/task-analysis-endpoint.test.ts +103 -0
- package/src/ui/server/endpoints/__tests__/upload-endpoints.test.ts +162 -96
- package/src/ui/server/endpoints/concurrency-endpoint.ts +2 -1
- package/src/ui/server/endpoints/create-pipeline-endpoint.ts +14 -29
- package/src/ui/server/endpoints/file-endpoints.ts +15 -10
- package/src/ui/server/endpoints/gate-endpoints.ts +2 -6
- package/src/ui/server/endpoints/job-control-endpoints.ts +129 -141
- package/src/ui/server/endpoints/job-endpoints.ts +7 -0
- package/src/ui/server/endpoints/meta-endpoint.ts +1 -1
- package/src/ui/server/endpoints/pipeline-analysis-endpoint.ts +99 -11
- package/src/ui/server/endpoints/schema-file-endpoint.ts +11 -3
- package/src/ui/server/endpoints/task-analysis-endpoint.ts +21 -5
- package/src/ui/server/endpoints/task-save-endpoint.ts +1 -2
- package/src/ui/server/endpoints/upload-endpoints.ts +5 -40
- package/src/ui/server/index.ts +19 -14
- package/src/ui/server/job-reader.ts +14 -2
- package/src/ui/server/router.ts +33 -10
- package/src/ui/server/sse-broadcast.ts +14 -3
- package/src/ui/server/sse-enhancer.ts +12 -2
- package/src/ui/state/__tests__/schema-loader.test.ts +11 -1
- package/src/ui/state/__tests__/snapshot.test.ts +120 -14
- package/src/ui/state/__tests__/types.test.ts +104 -5
- package/src/ui/state/snapshot.ts +2 -2
- package/src/ui/state/transformers/__tests__/list-transformer.test.ts +85 -2
- package/src/ui/state/transformers/__tests__/status-transformer.test.ts +250 -22
- package/src/ui/state/transformers/list-transformer.ts +13 -3
- package/src/ui/state/transformers/status-transformer.ts +36 -170
- package/src/ui/state/types.ts +15 -48
- package/src/utils/__tests__/path-containment.test.ts +160 -0
- package/src/{ui/server/utils → utils}/path-containment.ts +21 -0
- package/src/task-analysis/__tests__/enrichers-analysis-writer.test.ts +0 -86
- package/src/task-analysis/__tests__/enrichers-artifact-resolver.test.ts +0 -124
- package/src/task-analysis/__tests__/extractors-artifacts.test.ts +0 -133
- package/src/task-analysis/__tests__/extractors-llm-calls.test.ts +0 -46
- package/src/task-analysis/__tests__/extractors-stages.test.ts +0 -52
- package/src/task-analysis/__tests__/index.test.ts +0 -143
- package/src/task-analysis/__tests__/parser.test.ts +0 -41
- package/src/task-analysis/__tests__/utils-ast.test.ts +0 -82
- package/src/task-analysis/enrichers/analysis-writer.ts +0 -75
- package/src/task-analysis/enrichers/artifact-resolver.ts +0 -89
- package/src/task-analysis/extractors/artifacts.ts +0 -143
- package/src/task-analysis/extractors/llm-calls.ts +0 -117
- package/src/task-analysis/extractors/stages.ts +0 -45
- package/src/task-analysis/parser.ts +0 -20
- package/src/task-analysis/utils/ast.ts +0 -45
- package/src/ui/dist/assets/index--RH3sAt3.js.map +0 -1
- package/src/ui/dist/assets/style-CSSKuMOe.css +0 -2
- package/src/ui/embedded-assets.js +0 -12
- package/src/ui/server/__tests__/path-containment.test.ts +0 -54
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
tryParseJSON,
|
|
11
11
|
createProviderError,
|
|
12
12
|
ensureJsonResponseFormat,
|
|
13
|
+
isJsonResponseFormat,
|
|
13
14
|
} from "./base.ts";
|
|
14
15
|
import {
|
|
15
16
|
IdleTimeoutController,
|
|
@@ -25,7 +26,6 @@ const ANTHROPIC_VERSION = "2023-06-01";
|
|
|
25
26
|
const DEFAULT_MODEL = "claude-3-sonnet";
|
|
26
27
|
const DEFAULT_TEMPERATURE = 0.7;
|
|
27
28
|
const DEFAULT_MAX_TOKENS = 8192;
|
|
28
|
-
const DEFAULT_RESPONSE_FORMAT = "json";
|
|
29
29
|
const DEFAULT_MAX_RETRIES = 3;
|
|
30
30
|
|
|
31
31
|
export async function anthropicChat(
|
|
@@ -36,15 +36,17 @@ export async function anthropicChat(
|
|
|
36
36
|
model = DEFAULT_MODEL,
|
|
37
37
|
temperature = DEFAULT_TEMPERATURE,
|
|
38
38
|
maxTokens = DEFAULT_MAX_TOKENS,
|
|
39
|
-
responseFormat
|
|
39
|
+
responseFormat,
|
|
40
40
|
topP,
|
|
41
41
|
stop,
|
|
42
42
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
43
43
|
requestTimeoutMs,
|
|
44
44
|
} = options;
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const jsonMode = isJsonResponseFormat(responseFormat);
|
|
47
|
+
if (jsonMode) {
|
|
48
|
+
ensureJsonResponseFormat(responseFormat, "anthropic");
|
|
49
|
+
}
|
|
48
50
|
|
|
49
51
|
const { systemMsg, userMessages, assistantMessages } =
|
|
50
52
|
extractMessages(messages);
|
|
@@ -86,6 +88,7 @@ export async function anthropicChat(
|
|
|
86
88
|
model,
|
|
87
89
|
apiKey ?? "",
|
|
88
90
|
requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
91
|
+
jsonMode,
|
|
89
92
|
);
|
|
90
93
|
} catch (err) {
|
|
91
94
|
lastError = err;
|
|
@@ -109,6 +112,7 @@ async function anthropicStreamChat(
|
|
|
109
112
|
model: string,
|
|
110
113
|
apiKey: string,
|
|
111
114
|
timeoutMs: number,
|
|
115
|
+
jsonMode: boolean,
|
|
112
116
|
): Promise<AdapterResponse> {
|
|
113
117
|
const idle = new IdleTimeoutController(timeoutMs);
|
|
114
118
|
|
|
@@ -151,16 +155,24 @@ async function anthropicStreamChat(
|
|
|
151
155
|
const stripped = stripMarkdownFences(rawText);
|
|
152
156
|
const parsed = tryParseJSON(stripped);
|
|
153
157
|
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
if (jsonMode) {
|
|
159
|
+
if (typeof parsed === "string") {
|
|
160
|
+
throw new ProviderJsonParseError(
|
|
161
|
+
"anthropic",
|
|
162
|
+
model,
|
|
163
|
+
parsed.slice(0, 200),
|
|
164
|
+
undefined,
|
|
165
|
+
{
|
|
166
|
+
text: rawText,
|
|
167
|
+
usage,
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return { content: parsed as Record<string, unknown>, text: rawText, usage };
|
|
160
172
|
}
|
|
161
173
|
|
|
162
174
|
return {
|
|
163
|
-
content:
|
|
175
|
+
content: rawText,
|
|
164
176
|
text: rawText,
|
|
165
177
|
usage,
|
|
166
178
|
};
|
package/src/providers/base.ts
CHANGED
|
@@ -223,3 +223,22 @@ export function ensureJsonResponseFormat(
|
|
|
223
223
|
|
|
224
224
|
throw new ProviderJsonModeError(providerName);
|
|
225
225
|
}
|
|
226
|
+
|
|
227
|
+
export function isJsonResponseFormat(responseFormat: unknown): boolean {
|
|
228
|
+
if (typeof responseFormat === "string") {
|
|
229
|
+
return responseFormat === "json" || responseFormat === "json_object";
|
|
230
|
+
}
|
|
231
|
+
if (responseFormat && typeof responseFormat === "object") {
|
|
232
|
+
const obj = responseFormat as Record<string, unknown>;
|
|
233
|
+
return obj.type === "json_object" || obj.json_schema != null;
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function extractResponseSchema(responseFormat: unknown): unknown | undefined {
|
|
239
|
+
if (responseFormat && typeof responseFormat === "object") {
|
|
240
|
+
const obj = responseFormat as Record<string, unknown>;
|
|
241
|
+
if (obj.json_schema != null) return obj.json_schema;
|
|
242
|
+
}
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ensureJsonResponseFormat,
|
|
7
7
|
extractMessages,
|
|
8
8
|
isRetryableError,
|
|
9
|
+
isJsonResponseFormat,
|
|
9
10
|
sleep,
|
|
10
11
|
stripMarkdownFences,
|
|
11
12
|
tryParseJSON,
|
|
@@ -20,16 +21,9 @@ import type { ClaudeCodeOptions, AdapterResponse } from "./types.ts";
|
|
|
20
21
|
|
|
21
22
|
const DEFAULT_MODEL = "sonnet";
|
|
22
23
|
const DEFAULT_MAX_TURNS = 1;
|
|
23
|
-
const DEFAULT_RESPONSE_FORMAT = "json";
|
|
24
24
|
const DEFAULT_MAX_RETRIES = 3;
|
|
25
25
|
const AVAILABILITY_TIMEOUT_MS = 5_000;
|
|
26
26
|
|
|
27
|
-
const ZERO_USAGE = {
|
|
28
|
-
prompt_tokens: 0,
|
|
29
|
-
completion_tokens: 0,
|
|
30
|
-
total_tokens: 0,
|
|
31
|
-
} as const;
|
|
32
|
-
|
|
33
27
|
export async function claudeCodeChat(
|
|
34
28
|
options: ClaudeCodeOptions,
|
|
35
29
|
): Promise<AdapterResponse> {
|
|
@@ -38,11 +32,15 @@ export async function claudeCodeChat(
|
|
|
38
32
|
model = DEFAULT_MODEL,
|
|
39
33
|
maxTokens,
|
|
40
34
|
maxTurns = DEFAULT_MAX_TURNS,
|
|
41
|
-
responseFormat
|
|
35
|
+
responseFormat,
|
|
42
36
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
37
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
43
38
|
} = options;
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
const jsonMode = isJsonResponseFormat(responseFormat);
|
|
41
|
+
if (jsonMode) {
|
|
42
|
+
ensureJsonResponseFormat(responseFormat, "claudecode");
|
|
43
|
+
}
|
|
46
44
|
|
|
47
45
|
const { systemMsg, userMsg } = extractMessages(messages);
|
|
48
46
|
|
|
@@ -72,7 +70,7 @@ export async function claudeCodeChat(
|
|
|
72
70
|
stderr: "pipe",
|
|
73
71
|
});
|
|
74
72
|
|
|
75
|
-
const idle = new IdleTimeoutController(
|
|
73
|
+
const idle = new IdleTimeoutController(requestTimeoutMs);
|
|
76
74
|
|
|
77
75
|
// Kill the subprocess if the idle timeout fires
|
|
78
76
|
const onAbort = () => proc.kill();
|
|
@@ -94,18 +92,29 @@ export async function claudeCodeChat(
|
|
|
94
92
|
const cleanedText = stripMarkdownFences(accumulated.text);
|
|
95
93
|
const parsed = tryParseJSON(cleanedText);
|
|
96
94
|
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
if (jsonMode) {
|
|
96
|
+
if (typeof parsed === "string") {
|
|
97
|
+
throw new ProviderJsonParseError(
|
|
98
|
+
"claudecode",
|
|
99
|
+
model,
|
|
100
|
+
parsed.slice(0, 200),
|
|
101
|
+
undefined,
|
|
102
|
+
{
|
|
103
|
+
text: accumulated.text,
|
|
104
|
+
usage: accumulated.usage,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
content: parsed as Record<string, unknown>,
|
|
110
|
+
text: accumulated.text,
|
|
111
|
+
raw: accumulated.text,
|
|
112
|
+
};
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
return {
|
|
106
|
-
content:
|
|
116
|
+
content: accumulated.text,
|
|
107
117
|
text: accumulated.text,
|
|
108
|
-
usage: { ...ZERO_USAGE },
|
|
109
118
|
raw: accumulated.text,
|
|
110
119
|
};
|
|
111
120
|
} catch (err) {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
6
6
|
extractMessages,
|
|
7
7
|
isRetryableError,
|
|
8
|
+
isJsonResponseFormat,
|
|
8
9
|
sleep,
|
|
9
10
|
stripMarkdownFences,
|
|
10
11
|
tryParseJSON,
|
|
@@ -14,7 +15,6 @@ import { ProviderJsonParseError } from "./types.ts";
|
|
|
14
15
|
import type {
|
|
15
16
|
DeepSeekOptions,
|
|
16
17
|
AdapterResponse,
|
|
17
|
-
ResponseFormatObject,
|
|
18
18
|
} from "./types.ts";
|
|
19
19
|
import {
|
|
20
20
|
IdleTimeoutController,
|
|
@@ -26,26 +26,8 @@ import {
|
|
|
26
26
|
const DEEPSEEK_API_URL = "https://api.deepseek.com/chat/completions";
|
|
27
27
|
const DEFAULT_MODEL = "deepseek-chat";
|
|
28
28
|
const DEFAULT_TEMPERATURE = 0.7;
|
|
29
|
-
const DEFAULT_RESPONSE_FORMAT = "json_object";
|
|
30
29
|
const DEFAULT_MAX_RETRIES = 3;
|
|
31
30
|
|
|
32
|
-
/**
|
|
33
|
-
* Determines whether the response format indicates JSON mode.
|
|
34
|
-
* Returns true for "json", "json_object", { type: "json_object" }, { json_schema: ... }.
|
|
35
|
-
*/
|
|
36
|
-
function isJsonMode(
|
|
37
|
-
responseFormat: string | ResponseFormatObject | undefined,
|
|
38
|
-
): boolean {
|
|
39
|
-
if (!responseFormat) return false;
|
|
40
|
-
if (typeof responseFormat === "string") {
|
|
41
|
-
return responseFormat === "json" || responseFormat === "json_object";
|
|
42
|
-
}
|
|
43
|
-
return (
|
|
44
|
-
responseFormat.type === "json_object" ||
|
|
45
|
-
responseFormat.json_schema != null
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
31
|
export async function deepseekChat(
|
|
50
32
|
options: DeepSeekOptions,
|
|
51
33
|
): Promise<AdapterResponse> {
|
|
@@ -54,7 +36,7 @@ export async function deepseekChat(
|
|
|
54
36
|
model = DEFAULT_MODEL,
|
|
55
37
|
temperature = DEFAULT_TEMPERATURE,
|
|
56
38
|
maxTokens,
|
|
57
|
-
responseFormat
|
|
39
|
+
responseFormat,
|
|
58
40
|
topP,
|
|
59
41
|
stop,
|
|
60
42
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
@@ -66,7 +48,7 @@ export async function deepseekChat(
|
|
|
66
48
|
? Math.max(0, Math.trunc(maxRetries))
|
|
67
49
|
: DEFAULT_MAX_RETRIES;
|
|
68
50
|
|
|
69
|
-
const jsonMode =
|
|
51
|
+
const jsonMode = isJsonResponseFormat(responseFormat);
|
|
70
52
|
|
|
71
53
|
const { systemMsg, userMessages, assistantMessages } =
|
|
72
54
|
extractMessages(messages);
|
|
@@ -159,21 +141,20 @@ export async function deepseekChat(
|
|
|
159
141
|
"deepseek",
|
|
160
142
|
model,
|
|
161
143
|
parsed.slice(0, 200),
|
|
144
|
+
undefined,
|
|
145
|
+
{
|
|
146
|
+
text: rawText,
|
|
147
|
+
usage: accumulated.usage,
|
|
148
|
+
},
|
|
162
149
|
);
|
|
163
150
|
}
|
|
164
151
|
|
|
165
|
-
const usage = accumulated.usage ?? {
|
|
166
|
-
prompt_tokens: 0,
|
|
167
|
-
completion_tokens: 0,
|
|
168
|
-
total_tokens: 0,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
152
|
return {
|
|
172
153
|
content:
|
|
173
154
|
typeof parsed === "string"
|
|
174
155
|
? parsed
|
|
175
156
|
: (parsed as Record<string, unknown>),
|
|
176
|
-
usage,
|
|
157
|
+
usage: accumulated.usage,
|
|
177
158
|
};
|
|
178
159
|
} catch (err) {
|
|
179
160
|
lastError = err;
|
package/src/providers/gemini.ts
CHANGED
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
tryParseJSON,
|
|
11
11
|
createProviderError,
|
|
12
12
|
ensureJsonResponseFormat,
|
|
13
|
+
isJsonResponseFormat,
|
|
14
|
+
extractResponseSchema,
|
|
13
15
|
} from "./base.ts";
|
|
14
16
|
import { IdleTimeoutController, parseGeminiSse, accumulateStream } from "./stream-accumulator.ts";
|
|
15
17
|
import { ProviderJsonParseError } from "./types.ts";
|
|
16
18
|
import type {
|
|
17
19
|
GeminiOptions,
|
|
18
20
|
AdapterResponse,
|
|
19
|
-
ResponseFormatObject,
|
|
20
21
|
} from "./types.ts";
|
|
21
22
|
|
|
22
23
|
const DEFAULT_MODEL = "gemini-2.5-flash";
|
|
@@ -33,39 +34,6 @@ const SAFETY_SETTINGS = [
|
|
|
33
34
|
{ category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" },
|
|
34
35
|
];
|
|
35
36
|
|
|
36
|
-
/**
|
|
37
|
-
* Determines whether the response format indicates JSON mode.
|
|
38
|
-
* Returns true for "json", "json_object", { type: "json_object" }, { json_schema: ... }.
|
|
39
|
-
*/
|
|
40
|
-
function isJsonMode(
|
|
41
|
-
responseFormat: string | ResponseFormatObject | undefined,
|
|
42
|
-
): boolean {
|
|
43
|
-
if (!responseFormat) return false;
|
|
44
|
-
if (typeof responseFormat === "string") {
|
|
45
|
-
return responseFormat === "json" || responseFormat === "json_object";
|
|
46
|
-
}
|
|
47
|
-
return (
|
|
48
|
-
responseFormat.type === "json_object" ||
|
|
49
|
-
responseFormat.json_schema != null
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Extracts a JSON schema object from the responseFormat, if present.
|
|
55
|
-
*/
|
|
56
|
-
function extractJsonSchema(
|
|
57
|
-
responseFormat: string | ResponseFormatObject | undefined,
|
|
58
|
-
): unknown | undefined {
|
|
59
|
-
if (
|
|
60
|
-
responseFormat &&
|
|
61
|
-
typeof responseFormat === "object" &&
|
|
62
|
-
responseFormat.json_schema != null
|
|
63
|
-
) {
|
|
64
|
-
return responseFormat.json_schema;
|
|
65
|
-
}
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
37
|
export async function geminiChat(
|
|
70
38
|
options: GeminiOptions,
|
|
71
39
|
): Promise<AdapterResponse> {
|
|
@@ -84,12 +52,11 @@ export async function geminiChat(
|
|
|
84
52
|
presencePenalty: _presencePenalty,
|
|
85
53
|
} = options;
|
|
86
54
|
|
|
87
|
-
const jsonMode =
|
|
88
|
-
|
|
89
|
-
// Validate JSON response format if one was specified
|
|
90
|
-
if (responseFormat) {
|
|
55
|
+
const jsonMode = isJsonResponseFormat(responseFormat);
|
|
56
|
+
if (jsonMode) {
|
|
91
57
|
ensureJsonResponseFormat(responseFormat, "gemini");
|
|
92
58
|
}
|
|
59
|
+
const jsonSchema = extractResponseSchema(responseFormat);
|
|
93
60
|
|
|
94
61
|
const { systemMsg, userMessages, assistantMessages } =
|
|
95
62
|
extractMessages(messages);
|
|
@@ -108,25 +75,21 @@ export async function geminiChat(
|
|
|
108
75
|
parts: [{ text: m.content }],
|
|
109
76
|
}));
|
|
110
77
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
let systemText = systemMsg;
|
|
114
|
-
if (jsonSchema) {
|
|
115
|
-
const schemaStr = JSON.stringify(jsonSchema, null, 2);
|
|
116
|
-
const schemaInstruction = `\n\nRespond with JSON matching this schema:\n${schemaStr}`;
|
|
117
|
-
systemText = systemText ? systemText + schemaInstruction : schemaInstruction.trimStart();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const systemInstruction = systemText
|
|
121
|
-
? { parts: [{ text: systemText }] }
|
|
78
|
+
const systemInstruction = systemMsg
|
|
79
|
+
? { parts: [{ text: systemMsg }] }
|
|
122
80
|
: undefined;
|
|
123
81
|
|
|
124
|
-
// Build generationConfig
|
|
82
|
+
// Build generationConfig — wire native structured output when schema is present.
|
|
125
83
|
const generationConfig: Record<string, unknown> = { temperature };
|
|
126
84
|
if (maxTokens != null) generationConfig["maxOutputTokens"] = maxTokens;
|
|
127
85
|
if (topP != null) generationConfig["topP"] = topP;
|
|
128
86
|
if (stop != null) generationConfig["stopSequences"] = Array.isArray(stop) ? stop : [stop];
|
|
129
|
-
if (
|
|
87
|
+
if (jsonSchema != null) {
|
|
88
|
+
generationConfig["responseMimeType"] = "application/json";
|
|
89
|
+
generationConfig["responseSchema"] = jsonSchema;
|
|
90
|
+
} else if (jsonMode) {
|
|
91
|
+
generationConfig["responseMimeType"] = "application/json";
|
|
92
|
+
}
|
|
130
93
|
|
|
131
94
|
const baseUrl =
|
|
132
95
|
process.env["GEMINI_BASE_URL"] ?? DEFAULT_BASE_URL;
|
|
@@ -189,19 +152,18 @@ export async function geminiChat(
|
|
|
189
152
|
"gemini",
|
|
190
153
|
model,
|
|
191
154
|
parsed.slice(0, 200),
|
|
155
|
+
undefined,
|
|
156
|
+
{
|
|
157
|
+
text: rawText,
|
|
158
|
+
usage: streamUsage,
|
|
159
|
+
},
|
|
192
160
|
);
|
|
193
161
|
}
|
|
194
162
|
|
|
195
|
-
const usage = streamUsage ?? {
|
|
196
|
-
prompt_tokens: 0,
|
|
197
|
-
completion_tokens: 0,
|
|
198
|
-
total_tokens: 0,
|
|
199
|
-
};
|
|
200
|
-
|
|
201
163
|
return {
|
|
202
164
|
content: typeof parsed === "string" ? parsed : (parsed as Record<string, unknown>),
|
|
203
165
|
text: rawText,
|
|
204
|
-
usage,
|
|
166
|
+
usage: streamUsage,
|
|
205
167
|
};
|
|
206
168
|
} catch (err) {
|
|
207
169
|
lastError = err;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
extractMessages,
|
|
7
7
|
ensureMessagesPresent,
|
|
8
8
|
isRetryableError,
|
|
9
|
+
isJsonResponseFormat,
|
|
9
10
|
sleep,
|
|
10
11
|
stripMarkdownFences,
|
|
11
12
|
tryParseJSON,
|
|
@@ -52,6 +53,8 @@ export async function moonshotChat(
|
|
|
52
53
|
|
|
53
54
|
ensureMessagesPresent(messages, "moonshot");
|
|
54
55
|
|
|
56
|
+
const jsonMode = isJsonResponseFormat(responseFormat);
|
|
57
|
+
|
|
55
58
|
const { systemMsg, userMessages, assistantMessages } =
|
|
56
59
|
extractMessages(messages);
|
|
57
60
|
|
|
@@ -116,7 +119,7 @@ export async function moonshotChat(
|
|
|
116
119
|
return deepseekChat({
|
|
117
120
|
messages,
|
|
118
121
|
model: deepseekModel,
|
|
119
|
-
responseFormat
|
|
122
|
+
responseFormat,
|
|
120
123
|
requestTimeoutMs,
|
|
121
124
|
});
|
|
122
125
|
}
|
|
@@ -144,24 +147,25 @@ export async function moonshotChat(
|
|
|
144
147
|
const stripped = stripMarkdownFences(rawText);
|
|
145
148
|
const parsed = tryParseJSON(stripped);
|
|
146
149
|
|
|
147
|
-
|
|
148
|
-
if (typeof parsed === "string") {
|
|
150
|
+
if (jsonMode && typeof parsed === "string") {
|
|
149
151
|
throw new ProviderJsonParseError(
|
|
150
152
|
"moonshot",
|
|
151
153
|
model,
|
|
152
154
|
parsed.slice(0, 200),
|
|
155
|
+
undefined,
|
|
156
|
+
{
|
|
157
|
+
text: rawText,
|
|
158
|
+
usage: accumulated.usage,
|
|
159
|
+
},
|
|
153
160
|
);
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
const usage = accumulated.usage ?? {
|
|
157
|
-
prompt_tokens: 0,
|
|
158
|
-
completion_tokens: 0,
|
|
159
|
-
total_tokens: 0,
|
|
160
|
-
};
|
|
161
|
-
|
|
162
163
|
return {
|
|
163
|
-
content:
|
|
164
|
-
|
|
164
|
+
content:
|
|
165
|
+
typeof parsed === "string"
|
|
166
|
+
? parsed
|
|
167
|
+
: (parsed as Record<string, unknown>),
|
|
168
|
+
usage: accumulated.usage,
|
|
165
169
|
raw: { accumulated: rawText },
|
|
166
170
|
};
|
|
167
171
|
} catch (err) {
|