@jsonstudio/llms 0.4.4 → 0.4.5
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/conversion/codec-registry.js +11 -1
- package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
- package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
- package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
- package/dist/conversion/codecs/responses-openai-codec.js +9 -2
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
- package/dist/conversion/hub/node-support.js +3 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
- package/dist/conversion/hub/response/provider-response.js +1 -1
- package/dist/conversion/hub/response/response-mappers.js +1 -1
- package/dist/conversion/hub/response/response-runtime.js +109 -10
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
- package/dist/conversion/hub/standardized-bridge.js +3 -0
- package/dist/conversion/hub/tool-governance/rules.js +2 -2
- package/dist/conversion/index.d.ts +5 -0
- package/dist/conversion/index.js +5 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
- package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
- package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
- package/dist/conversion/pipeline/index.d.ts +35 -0
- package/dist/conversion/pipeline/index.js +103 -0
- package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
- package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
- package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
- package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
- package/dist/conversion/pipeline/schema/index.d.ts +1 -0
- package/dist/conversion/pipeline/schema/index.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
- package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
- package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
- package/dist/conversion/shared/anthropic-message-utils.js +587 -0
- package/dist/conversion/shared/bridge-actions.d.ts +39 -0
- package/dist/conversion/shared/bridge-actions.js +709 -0
- package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
- package/dist/conversion/shared/bridge-conversation-store.js +279 -0
- package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
- package/dist/conversion/shared/bridge-id-utils.js +42 -0
- package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
- package/dist/conversion/shared/bridge-instructions.js +113 -0
- package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
- package/dist/conversion/shared/bridge-message-types.js +1 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
- package/dist/conversion/shared/bridge-message-utils.js +473 -0
- package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
- package/dist/conversion/shared/bridge-metadata.js +1 -0
- package/dist/conversion/shared/bridge-policies.d.ts +18 -0
- package/dist/conversion/shared/bridge-policies.js +276 -0
- package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/bridge-request-adapter.js +430 -0
- package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
- package/dist/conversion/shared/chat-output-normalizer.js +56 -0
- package/dist/conversion/shared/chat-request-filters.js +24 -1
- package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
- package/dist/conversion/shared/gemini-tool-utils.js +130 -0
- package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
- package/dist/conversion/shared/metadata-passthrough.js +57 -0
- package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
- package/dist/conversion/shared/output-content-normalizer.js +119 -0
- package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
- package/dist/conversion/shared/reasoning-normalizer.js +368 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
- package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
- package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
- package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
- package/dist/conversion/shared/reasoning-utils.js +42 -0
- package/dist/conversion/shared/responses-conversation-store.js +5 -11
- package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
- package/dist/conversion/shared/responses-message-utils.js +206 -0
- package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
- package/dist/conversion/shared/responses-output-builder.js +179 -0
- package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
- package/dist/conversion/shared/responses-output-utils.js +108 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/responses-request-adapter.js +9 -40
- package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
- package/dist/conversion/shared/responses-response-utils.js +209 -0
- package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
- package/dist/conversion/shared/responses-tool-utils.js +90 -0
- package/dist/conversion/shared/responses-types.d.ts +33 -0
- package/dist/conversion/shared/responses-types.js +1 -0
- package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
- package/dist/conversion/shared/tool-call-utils.js +56 -0
- package/dist/conversion/shared/tool-mapping.d.ts +19 -0
- package/dist/conversion/shared/tool-mapping.js +124 -0
- package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
- package/dist/conversion/shared/tool-normalizers.js +84 -0
- package/dist/router/virtual-router/bootstrap.js +18 -3
- package/dist/router/virtual-router/provider-registry.js +4 -2
- package/dist/router/virtual-router/types.d.ts +212 -0
- package/dist/sse/index.d.ts +38 -2
- package/dist/sse/index.js +27 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
- package/dist/sse/json-to-sse/index.d.ts +7 -0
- package/dist/sse/json-to-sse/index.js +2 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
- package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
- package/dist/sse/registry/sse-codec-registry.js +30 -1
- package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
- package/dist/sse/shared/reasoning-dispatcher.js +25 -0
- package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
- package/dist/sse/shared/responses-output-normalizer.js +45 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
- package/dist/sse/shared/serializers/index.d.ts +41 -0
- package/dist/sse/shared/serializers/index.js +2 -0
- package/dist/sse/shared/writer.d.ts +127 -0
- package/dist/sse/shared/writer.js +37 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
- package/dist/sse/sse-to-json/index.d.ts +7 -0
- package/dist/sse/sse-to-json/index.js +2 -0
- package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
- package/dist/sse/types/anthropic-types.d.ts +170 -0
- package/dist/sse/types/anthropic-types.js +8 -5
- package/dist/sse/types/chat-types.d.ts +10 -0
- package/dist/sse/types/chat-types.js +2 -1
- package/dist/sse/types/core-interfaces.d.ts +1 -1
- package/dist/sse/types/gemini-types.d.ts +116 -0
- package/dist/sse/types/gemini-types.js +5 -0
- package/dist/sse/types/index.d.ts +5 -2
- package/dist/sse/types/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { extractToolCallsFromReasoningText } from './reasoning-tool-parser.js';
|
|
2
|
+
import { mergeToolCalls } from './tool-call-utils.js';
|
|
3
|
+
const THINK_TAG_PATTERN = /<\/?think[^>]*>/gi;
|
|
4
|
+
const REFLECTION_TAG_PATTERN = /<\/?reflection[^>]*>/gi;
|
|
5
|
+
function flattenReasoning(value, depth = 0) {
|
|
6
|
+
if (depth > 4 || value == null) {
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value
|
|
14
|
+
.map((item) => flattenReasoning(item, depth + 1))
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.join('\n');
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'object') {
|
|
19
|
+
const record = value;
|
|
20
|
+
if (typeof record.text === 'string') {
|
|
21
|
+
return record.text;
|
|
22
|
+
}
|
|
23
|
+
if (typeof record.content === 'string') {
|
|
24
|
+
return record.content;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(record.content) || typeof record.content === 'object') {
|
|
27
|
+
return flattenReasoning(record.content, depth + 1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
function collectReasoningFragments(message) {
|
|
33
|
+
const fragments = [];
|
|
34
|
+
if ('reasoning_content' in message) {
|
|
35
|
+
const text = flattenReasoning(message.reasoning_content);
|
|
36
|
+
if (text) {
|
|
37
|
+
fragments.push(text);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if ('reasoning' in message) {
|
|
41
|
+
const text = flattenReasoning(message.reasoning);
|
|
42
|
+
if (text) {
|
|
43
|
+
fragments.push(text);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return fragments;
|
|
47
|
+
}
|
|
48
|
+
function writeReasoningContent(message, text) {
|
|
49
|
+
const trimmed = text.trim();
|
|
50
|
+
if (trimmed.length) {
|
|
51
|
+
message.reasoning_content = trimmed;
|
|
52
|
+
}
|
|
53
|
+
else if (Object.prototype.hasOwnProperty.call(message, 'reasoning_content')) {
|
|
54
|
+
delete message.reasoning_content;
|
|
55
|
+
}
|
|
56
|
+
if (typeof message.reasoning === 'string') {
|
|
57
|
+
if (trimmed.length) {
|
|
58
|
+
message.reasoning = trimmed;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
delete message.reasoning;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function stripReasoningTags(text) {
|
|
66
|
+
if (!text) {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
return text.replace(THINK_TAG_PATTERN, '').replace(REFLECTION_TAG_PATTERN, '').trim();
|
|
70
|
+
}
|
|
71
|
+
export function normalizeMessageReasoningTools(message, options) {
|
|
72
|
+
if (!message || typeof message !== 'object') {
|
|
73
|
+
return { toolCallsAdded: 0 };
|
|
74
|
+
}
|
|
75
|
+
const fragments = collectReasoningFragments(message);
|
|
76
|
+
if (!fragments.length) {
|
|
77
|
+
const existing = typeof message.reasoning_content === 'string'
|
|
78
|
+
? message.reasoning_content.trim()
|
|
79
|
+
: '';
|
|
80
|
+
if (!existing && Object.prototype.hasOwnProperty.call(message, 'reasoning_content')) {
|
|
81
|
+
delete message.reasoning_content;
|
|
82
|
+
}
|
|
83
|
+
return { toolCallsAdded: 0 };
|
|
84
|
+
}
|
|
85
|
+
const combined = fragments.join('\n').trim();
|
|
86
|
+
if (!combined.length) {
|
|
87
|
+
writeReasoningContent(message, '');
|
|
88
|
+
return { toolCallsAdded: 0 };
|
|
89
|
+
}
|
|
90
|
+
const sanitized = stripReasoningTags(combined);
|
|
91
|
+
if (!sanitized.length) {
|
|
92
|
+
writeReasoningContent(message, '');
|
|
93
|
+
return { toolCallsAdded: 0 };
|
|
94
|
+
}
|
|
95
|
+
const idPrefix = options?.idPrefix ?? 'reasoning';
|
|
96
|
+
const { cleanedText, toolCalls } = extractToolCallsFromReasoningText(sanitized, { idPrefix });
|
|
97
|
+
const trimmed = (cleanedText || '').trim();
|
|
98
|
+
writeReasoningContent(message, trimmed);
|
|
99
|
+
if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
|
|
100
|
+
return { toolCallsAdded: 0, cleanedReasoning: trimmed };
|
|
101
|
+
}
|
|
102
|
+
const current = Array.isArray(message.tool_calls)
|
|
103
|
+
? message.tool_calls
|
|
104
|
+
: undefined;
|
|
105
|
+
const merged = mergeToolCalls(current, toolCalls);
|
|
106
|
+
message.tool_calls = merged;
|
|
107
|
+
return {
|
|
108
|
+
toolCallsAdded: merged.length - (current?.length ?? 0),
|
|
109
|
+
cleanedReasoning: trimmed
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function normalizeChatResponseReasoningTools(response, options) {
|
|
113
|
+
if (!response || typeof response !== 'object') {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const choices = Array.isArray(response.choices)
|
|
117
|
+
? response.choices
|
|
118
|
+
: [];
|
|
119
|
+
for (let i = 0; i < choices.length; i++) {
|
|
120
|
+
const choice = choices[i];
|
|
121
|
+
if (!choice || typeof choice !== 'object') {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const message = choice.message;
|
|
125
|
+
if (!message || typeof message !== 'object') {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
normalizeMessageReasoningTools(message, {
|
|
129
|
+
idPrefix: `${options?.idPrefixBase ?? 'reasoning_choice'}_${i + 1}`
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type ToolCallRecord = Record<string, unknown>;
|
|
2
|
+
interface ExtractionOptions {
|
|
3
|
+
idPrefix?: string;
|
|
4
|
+
}
|
|
5
|
+
interface ExtractionResult {
|
|
6
|
+
cleanedText: string;
|
|
7
|
+
toolCalls: ToolCallRecord[];
|
|
8
|
+
}
|
|
9
|
+
export declare function extractToolCallsFromReasoningText(text: string, options?: ExtractionOptions): ExtractionResult;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { repairArgumentsToString, parseLenient } from './jsonish.js';
|
|
2
|
+
const FENCE_PATTERN = /```(?:tool_call|tool_calls|function_call|json)?\s*([\s\S]*?)\s*```/gi;
|
|
3
|
+
const TAG_PATTERN = /<(tool_call|function_call)(?:\s+name="([^"]+)")?\s*>([\s\S]*?)<\/\1>/gi;
|
|
4
|
+
const BRACKET_PATTERN = /\[(tool_call|function_call)(?:\s+name="([^"]+)")?\]([\s\S]*?)\[\/\1\]/gi;
|
|
5
|
+
const LABEL_PATTERN = /(tool_call|function_call)\s*[:=]\s*({[\s\S]+?})/gi;
|
|
6
|
+
export function extractToolCallsFromReasoningText(text, options) {
|
|
7
|
+
if (typeof text !== 'string' || !text.trim()) {
|
|
8
|
+
return { cleanedText: typeof text === 'string' ? text : '', toolCalls: [] };
|
|
9
|
+
}
|
|
10
|
+
const idPrefix = options?.idPrefix ?? 'reasoning';
|
|
11
|
+
const matches = [];
|
|
12
|
+
const consume = (pattern, getPayload) => {
|
|
13
|
+
pattern.lastIndex = 0;
|
|
14
|
+
let match;
|
|
15
|
+
while ((match = pattern.exec(text))) {
|
|
16
|
+
const payload = getPayload(match);
|
|
17
|
+
if (!payload) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const parsed = parseToolCall(payload.body, payload.nameHint);
|
|
21
|
+
if (!parsed) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
matches.push({ start: match.index, end: match.index + match[0].length, call: parsed });
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
consume(FENCE_PATTERN, match => ({ body: match[1] ?? '' }));
|
|
28
|
+
consume(TAG_PATTERN, match => ({ body: match[3] ?? '', nameHint: match[2] }));
|
|
29
|
+
consume(BRACKET_PATTERN, match => ({ body: match[3] ?? '', nameHint: match[2] }));
|
|
30
|
+
consume(LABEL_PATTERN, match => ({ body: match[2] ?? '' }));
|
|
31
|
+
if (!matches.length) {
|
|
32
|
+
const parsed = parseToolCall(text);
|
|
33
|
+
if (parsed) {
|
|
34
|
+
return {
|
|
35
|
+
cleanedText: '',
|
|
36
|
+
toolCalls: [buildToolCall(parsed, idPrefix, 0)]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return { cleanedText: text, toolCalls: [] };
|
|
40
|
+
}
|
|
41
|
+
const toolCalls = matches.map((entry, index) => buildToolCall(entry.call, idPrefix, index));
|
|
42
|
+
matches.sort((a, b) => b.start - a.start);
|
|
43
|
+
let cleaned = text;
|
|
44
|
+
for (const entry of matches) {
|
|
45
|
+
cleaned = cleaned.slice(0, entry.start) + cleaned.slice(entry.end);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
cleanedText: cleaned.trim(),
|
|
49
|
+
toolCalls
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function parseToolCall(raw, nameHint) {
|
|
53
|
+
if (!raw || typeof raw !== 'string') {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const trimmed = raw.trim();
|
|
57
|
+
if (!trimmed) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const parsed = parseLenient(trimmed);
|
|
61
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const candidateName = parsed.name ??
|
|
65
|
+
parsed.function?.name ??
|
|
66
|
+
parsed.tool_name ??
|
|
67
|
+
parsed.tool ??
|
|
68
|
+
nameHint;
|
|
69
|
+
const name = typeof candidateName === 'string' ? candidateName.trim() : undefined;
|
|
70
|
+
if (!name) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const argsSource = parsed.function?.arguments ??
|
|
74
|
+
parsed.arguments ??
|
|
75
|
+
parsed.input ??
|
|
76
|
+
parsed.params ??
|
|
77
|
+
parsed.parameters ??
|
|
78
|
+
parsed.payload ??
|
|
79
|
+
{};
|
|
80
|
+
const args = repairArgumentsToString(argsSource);
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
args
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function buildToolCall(parsed, prefix, index) {
|
|
87
|
+
return {
|
|
88
|
+
id: `${prefix}_${index + 1}`,
|
|
89
|
+
type: 'function',
|
|
90
|
+
function: {
|
|
91
|
+
name: parsed.name,
|
|
92
|
+
arguments: parsed.args
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function extractReasoningSegments(source, reasoningCollector) {
|
|
2
|
+
let working = source ?? '';
|
|
3
|
+
const hasExplicitOpen = /<think>/i.test(source) ||
|
|
4
|
+
/<reflection>/i.test(source) ||
|
|
5
|
+
/```\s*(?:think|reflection)/i.test(source);
|
|
6
|
+
const hasExplicitClose = /<\/think>/i.test(source) || /<\/reflection>/i.test(source);
|
|
7
|
+
const push = (value) => {
|
|
8
|
+
const trimmed = (value ?? '').trim();
|
|
9
|
+
if (trimmed && reasoningCollector) {
|
|
10
|
+
reasoningCollector.push(trimmed);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const patterns = [
|
|
14
|
+
/<think>([\s\S]*?)<\/think>/gi,
|
|
15
|
+
/<reflection>([\s\S]*?)<\/reflection>/gi,
|
|
16
|
+
/```\s*(?:think|reflection)[\s\S]*?```/gi
|
|
17
|
+
];
|
|
18
|
+
for (const pattern of patterns) {
|
|
19
|
+
working = working.replace(pattern, (_match, inner) => {
|
|
20
|
+
push(inner);
|
|
21
|
+
return '';
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
working = working.replace(/<\/?think>/gi, '').replace(/<\/?reflection>/gi, '');
|
|
25
|
+
working = working.replace(/\n{3,}/g, '\n\n');
|
|
26
|
+
if (reasoningCollector && !hasExplicitOpen && hasExplicitClose) {
|
|
27
|
+
const trimmed = working.trim();
|
|
28
|
+
if (trimmed.length) {
|
|
29
|
+
reasoningCollector.push(trimmed);
|
|
30
|
+
}
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
return working.trim();
|
|
34
|
+
}
|
|
35
|
+
export function sanitizeReasoningTaggedText(value) {
|
|
36
|
+
try {
|
|
37
|
+
return extractReasoningSegments(value);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { normalizeFunctionCallOutputId } from './responses-id-utils.js';
|
|
2
1
|
const TTL_MS = 1000 * 60 * 30; // 30 minutes safety window
|
|
3
2
|
function cloneDeep(value) {
|
|
4
3
|
try {
|
|
@@ -114,9 +113,8 @@ function normalizeSubmittedToolOutputs(toolOutputs) {
|
|
|
114
113
|
: typeof rec.id === 'string'
|
|
115
114
|
? rec.id
|
|
116
115
|
: undefined;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const callId = rawId.trim();
|
|
116
|
+
const trimmed = typeof rawId === 'string' ? rawId.trim() : '';
|
|
117
|
+
const callId = trimmed.length ? trimmed : `fc_resume_${index}`;
|
|
120
118
|
const outputValue = rec.output ?? null;
|
|
121
119
|
const normalizedOutput = typeof outputValue === 'string'
|
|
122
120
|
? outputValue
|
|
@@ -128,17 +126,13 @@ function normalizeSubmittedToolOutputs(toolOutputs) {
|
|
|
128
126
|
return String(outputValue ?? '');
|
|
129
127
|
}
|
|
130
128
|
})();
|
|
131
|
-
const identifier = normalizeFunctionCallOutputId({
|
|
132
|
-
callId,
|
|
133
|
-
fallback: `fc_resume_${index}`
|
|
134
|
-
});
|
|
135
129
|
items.push({
|
|
136
130
|
type: 'function_call_output',
|
|
137
|
-
id:
|
|
138
|
-
call_id:
|
|
131
|
+
id: callId,
|
|
132
|
+
call_id: callId,
|
|
139
133
|
output: normalizedOutput
|
|
140
134
|
});
|
|
141
|
-
submitted.push({ callId
|
|
135
|
+
submitted.push({ callId, originalId: rawId ?? callId, outputText: normalizedOutput });
|
|
142
136
|
});
|
|
143
137
|
return { items, submitted };
|
|
144
138
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ResponsesInputItem } from './responses-types.js';
|
|
2
|
+
export declare function normalizeResponseRole(role: unknown): string;
|
|
3
|
+
export declare function normalizeArgumentsBySchema(argsStringOrObj: unknown, _functionName: string | undefined, _tools: unknown): string;
|
|
4
|
+
export declare function normalizeToolOutput(entry: ResponsesInputItem): string | null;
|
|
5
|
+
export interface BuildResponsesInputOptions {
|
|
6
|
+
messages: Array<Record<string, unknown>>;
|
|
7
|
+
tools?: Array<Record<string, unknown>> | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface BuildResponsesInputResult {
|
|
10
|
+
input: ResponsesInputItem[];
|
|
11
|
+
combinedSystemInstruction?: string;
|
|
12
|
+
latestUserInstruction?: string;
|
|
13
|
+
originalSystemMessages: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function buildResponsesInputFromChatMessages(options: BuildResponsesInputOptions): BuildResponsesInputResult;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { normalizeFunctionCallId, normalizeFunctionCallOutputId } from './responses-id-utils.js';
|
|
2
|
+
export function normalizeResponseRole(role) {
|
|
3
|
+
if (typeof role === 'string') {
|
|
4
|
+
const normalized = role.toLowerCase();
|
|
5
|
+
if (normalized === 'system' || normalized === 'assistant' || normalized === 'user' || normalized === 'tool') {
|
|
6
|
+
return normalized;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return 'user';
|
|
10
|
+
}
|
|
11
|
+
export function normalizeArgumentsBySchema(argsStringOrObj, _functionName, _tools) {
|
|
12
|
+
if (typeof argsStringOrObj === 'string')
|
|
13
|
+
return argsStringOrObj;
|
|
14
|
+
try {
|
|
15
|
+
return JSON.stringify(argsStringOrObj ?? {});
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return String(argsStringOrObj);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function normalizeToolOutput(entry) {
|
|
22
|
+
const out = entry?.output;
|
|
23
|
+
if (typeof out === 'string')
|
|
24
|
+
return out;
|
|
25
|
+
if (out && typeof out === 'object') {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.stringify(out);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return String(out);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function collectText(value) {
|
|
36
|
+
if (!value)
|
|
37
|
+
return '';
|
|
38
|
+
if (typeof value === 'string')
|
|
39
|
+
return value;
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
return value.map(part => collectText(part)).join('');
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'object') {
|
|
44
|
+
const record = value;
|
|
45
|
+
if (typeof record.text === 'string')
|
|
46
|
+
return record.text;
|
|
47
|
+
if (Array.isArray(record.content))
|
|
48
|
+
return collectText(record.content);
|
|
49
|
+
if (typeof record.content === 'string')
|
|
50
|
+
return record.content;
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
function extractUserTextFromEntry(entry) {
|
|
55
|
+
if (!entry || typeof entry !== 'object')
|
|
56
|
+
return '';
|
|
57
|
+
const directContent = entry.content ?? entry.message?.content;
|
|
58
|
+
if (typeof directContent === 'string')
|
|
59
|
+
return directContent.trim();
|
|
60
|
+
if (Array.isArray(directContent)) {
|
|
61
|
+
return directContent.map(block => collectText(block)).join('').trim();
|
|
62
|
+
}
|
|
63
|
+
const text = entry.text ?? entry.message?.text;
|
|
64
|
+
if (typeof text === 'string')
|
|
65
|
+
return text.trim();
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
export function buildResponsesInputFromChatMessages(options) {
|
|
69
|
+
const { messages, tools } = options;
|
|
70
|
+
const input = [];
|
|
71
|
+
const systemParts = [];
|
|
72
|
+
const originalSystemMessages = [];
|
|
73
|
+
let latestUserInstruction = null;
|
|
74
|
+
const pendingToolCallIds = [];
|
|
75
|
+
const knownToolCallIds = new Set();
|
|
76
|
+
const toolCallIdAliases = new Map();
|
|
77
|
+
for (const m of messages) {
|
|
78
|
+
if (!m || typeof m !== 'object')
|
|
79
|
+
continue;
|
|
80
|
+
const role = normalizeResponseRole(m.role || 'user');
|
|
81
|
+
const content = m.content;
|
|
82
|
+
const collectedText = collectText(content);
|
|
83
|
+
const text = role === 'system' ? collectedText : collectedText.trim();
|
|
84
|
+
if (role === 'system') {
|
|
85
|
+
if (collectedText && collectedText.length) {
|
|
86
|
+
originalSystemMessages.push(collectedText);
|
|
87
|
+
systemParts.push(collectedText);
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (role === 'tool') {
|
|
92
|
+
const rawToolId = m.tool_call_id ||
|
|
93
|
+
m.call_id ||
|
|
94
|
+
m.tool_use_id ||
|
|
95
|
+
m.id ||
|
|
96
|
+
undefined;
|
|
97
|
+
let callId = typeof rawToolId === 'string' ? rawToolId : undefined;
|
|
98
|
+
if (callId && toolCallIdAliases.has(callId)) {
|
|
99
|
+
callId = toolCallIdAliases.get(callId);
|
|
100
|
+
}
|
|
101
|
+
if (!callId || !knownToolCallIds.has(callId)) {
|
|
102
|
+
const fallbackId = pendingToolCallIds.length ? pendingToolCallIds.shift() : undefined;
|
|
103
|
+
if (fallbackId) {
|
|
104
|
+
if (callId && callId !== fallbackId) {
|
|
105
|
+
toolCallIdAliases.set(callId, fallbackId);
|
|
106
|
+
}
|
|
107
|
+
callId = fallbackId;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (callId) {
|
|
111
|
+
const idx = pendingToolCallIds.indexOf(callId);
|
|
112
|
+
if (idx >= 0)
|
|
113
|
+
pendingToolCallIds.splice(idx, 1);
|
|
114
|
+
}
|
|
115
|
+
const normalizedCallId = normalizeFunctionCallId({
|
|
116
|
+
callId,
|
|
117
|
+
fallback: `fc_call_${input.length + 1}`
|
|
118
|
+
});
|
|
119
|
+
if (callId && callId !== normalizedCallId) {
|
|
120
|
+
toolCallIdAliases.set(callId, normalizedCallId);
|
|
121
|
+
}
|
|
122
|
+
const normalizedId = normalizeFunctionCallOutputId({
|
|
123
|
+
callId: normalizedCallId,
|
|
124
|
+
fallback: `fc_tool_${input.length + 1}`
|
|
125
|
+
});
|
|
126
|
+
const entry = {
|
|
127
|
+
type: 'function_call_output',
|
|
128
|
+
id: normalizedId,
|
|
129
|
+
call_id: normalizedCallId,
|
|
130
|
+
output: text || undefined
|
|
131
|
+
};
|
|
132
|
+
input.push(entry);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const toolCalls = Array.isArray(m.tool_calls) ? m.tool_calls : [];
|
|
136
|
+
if (toolCalls.length) {
|
|
137
|
+
for (const tc of toolCalls) {
|
|
138
|
+
try {
|
|
139
|
+
const rawId = typeof tc?.id === 'string' && tc.id
|
|
140
|
+
? String(tc.id)
|
|
141
|
+
: `call_${Math.random().toString(36).slice(2, 8)}`;
|
|
142
|
+
const normalizedCallId = normalizeFunctionCallId({
|
|
143
|
+
callId: rawId,
|
|
144
|
+
fallback: `fc_call_${input.length + 1}`
|
|
145
|
+
});
|
|
146
|
+
if (rawId !== normalizedCallId) {
|
|
147
|
+
toolCallIdAliases.set(rawId, normalizedCallId);
|
|
148
|
+
}
|
|
149
|
+
const fn = tc?.function || {};
|
|
150
|
+
const name = typeof fn?.name === 'string' ? String(fn.name) : 'tool';
|
|
151
|
+
const argsRaw = fn?.arguments;
|
|
152
|
+
const args = typeof argsRaw === 'string'
|
|
153
|
+
? argsRaw
|
|
154
|
+
: normalizeArgumentsBySchema(argsRaw, name, tools);
|
|
155
|
+
const entry = {
|
|
156
|
+
type: 'function_call',
|
|
157
|
+
id: normalizedCallId,
|
|
158
|
+
call_id: normalizedCallId,
|
|
159
|
+
name,
|
|
160
|
+
arguments: args
|
|
161
|
+
};
|
|
162
|
+
input.push(entry);
|
|
163
|
+
knownToolCallIds.add(normalizedCallId);
|
|
164
|
+
pendingToolCallIds.push(normalizedCallId);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// ignore malformed tool_call
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (text) {
|
|
173
|
+
const tRole = role === 'assistant' ? 'output_text' : 'input_text';
|
|
174
|
+
const entry = {
|
|
175
|
+
type: 'message',
|
|
176
|
+
role,
|
|
177
|
+
content: [{ type: tRole, text }]
|
|
178
|
+
};
|
|
179
|
+
input.push(entry);
|
|
180
|
+
if (role === 'user') {
|
|
181
|
+
latestUserInstruction = text;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!latestUserInstruction) {
|
|
186
|
+
for (let i = input.length - 1; i >= 0; i -= 1) {
|
|
187
|
+
const entry = input[i];
|
|
188
|
+
const roleSource = entry?.role ?? entry?.message?.role;
|
|
189
|
+
const role = typeof roleSource === 'string' ? roleSource.toLowerCase() : '';
|
|
190
|
+
if (role !== 'user')
|
|
191
|
+
continue;
|
|
192
|
+
const text = extractUserTextFromEntry(entry);
|
|
193
|
+
if (text) {
|
|
194
|
+
latestUserInstruction = text;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const combinedSystemInstruction = systemParts.join('\n\n').trim();
|
|
200
|
+
return {
|
|
201
|
+
input,
|
|
202
|
+
combinedSystemInstruction: combinedSystemInstruction.length ? combinedSystemInstruction : undefined,
|
|
203
|
+
latestUserInstruction: latestUserInstruction || undefined,
|
|
204
|
+
originalSystemMessages
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ResponsesOutputItem } from '../../sse/types/index.js';
|
|
2
|
+
export interface BuildResponsesOutputOptions {
|
|
3
|
+
response: Record<string, unknown>;
|
|
4
|
+
message?: Record<string, unknown>;
|
|
5
|
+
requestId?: string;
|
|
6
|
+
sanitizeFunctionName: (raw: unknown) => string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface BuildResponsesOutputResult {
|
|
9
|
+
outputItems: ResponsesOutputItem[];
|
|
10
|
+
outputText: string;
|
|
11
|
+
status: string;
|
|
12
|
+
requiredAction?: Record<string, unknown>;
|
|
13
|
+
usage?: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare function buildResponsesOutputFromChat(options: BuildResponsesOutputOptions): BuildResponsesOutputResult;
|