@jackchen_me/open-multi-agent 0.1.0 → 1.0.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/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/pull_request_template.md +14 -0
- package/.github/workflows/ci.yml +23 -0
- package/CLAUDE.md +80 -0
- package/CODE_OF_CONDUCT.md +48 -0
- package/CONTRIBUTING.md +72 -0
- package/DECISIONS.md +43 -0
- package/README.md +144 -144
- package/README_zh.md +277 -0
- package/SECURITY.md +17 -0
- package/dist/agent/agent.d.ts +20 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +233 -12
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/loop-detector.d.ts +39 -0
- package/dist/agent/loop-detector.d.ts.map +1 -0
- package/dist/agent/loop-detector.js +122 -0
- package/dist/agent/loop-detector.js.map +1 -0
- package/dist/agent/pool.d.ts +2 -1
- package/dist/agent/pool.d.ts.map +1 -1
- package/dist/agent/pool.js +4 -2
- package/dist/agent/pool.js.map +1 -1
- package/dist/agent/runner.d.ts +23 -1
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +113 -12
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/structured-output.d.ts +33 -0
- package/dist/agent/structured-output.d.ts.map +1 -0
- package/dist/agent/structured-output.js +116 -0
- package/dist/agent/structured-output.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/adapter.d.ts +12 -4
- package/dist/llm/adapter.d.ts.map +1 -1
- package/dist/llm/adapter.js +28 -5
- package/dist/llm/adapter.js.map +1 -1
- package/dist/llm/anthropic.d.ts +1 -1
- package/dist/llm/anthropic.d.ts.map +1 -1
- package/dist/llm/anthropic.js +2 -1
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/copilot.d.ts +92 -0
- package/dist/llm/copilot.d.ts.map +1 -0
- package/dist/llm/copilot.js +427 -0
- package/dist/llm/copilot.js.map +1 -0
- package/dist/llm/gemini.d.ts +65 -0
- package/dist/llm/gemini.d.ts.map +1 -0
- package/dist/llm/gemini.js +317 -0
- package/dist/llm/gemini.js.map +1 -0
- package/dist/llm/grok.d.ts +21 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +24 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/openai-common.d.ts +54 -0
- package/dist/llm/openai-common.d.ts.map +1 -0
- package/dist/llm/openai-common.js +242 -0
- package/dist/llm/openai-common.js.map +1 -0
- package/dist/llm/openai.d.ts +2 -2
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/llm/openai.js +23 -226
- package/dist/llm/openai.js.map +1 -1
- package/dist/orchestrator/orchestrator.d.ts +25 -1
- package/dist/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.js +214 -41
- package/dist/orchestrator/orchestrator.js.map +1 -1
- package/dist/task/queue.d.ts +31 -2
- package/dist/task/queue.d.ts.map +1 -1
- package/dist/task/queue.js +70 -3
- package/dist/task/queue.js.map +1 -1
- package/dist/task/task.d.ts +3 -0
- package/dist/task/task.d.ts.map +1 -1
- package/dist/task/task.js +5 -1
- package/dist/task/task.js.map +1 -1
- package/dist/team/messaging.d.ts.map +1 -1
- package/dist/team/messaging.js +2 -1
- package/dist/team/messaging.js.map +1 -1
- package/dist/tool/text-tool-extractor.d.ts +32 -0
- package/dist/tool/text-tool-extractor.d.ts.map +1 -0
- package/dist/tool/text-tool-extractor.js +187 -0
- package/dist/tool/text-tool-extractor.js.map +1 -0
- package/dist/types.d.ts +167 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/trace.d.ts +12 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +30 -0
- package/dist/utils/trace.js.map +1 -0
- package/examples/05-copilot-test.ts +49 -0
- package/examples/06-local-model.ts +200 -0
- package/examples/07-fan-out-aggregate.ts +209 -0
- package/examples/08-gemma4-local.ts +192 -0
- package/examples/09-structured-output.ts +73 -0
- package/examples/10-task-retry.ts +132 -0
- package/examples/11-trace-observability.ts +133 -0
- package/examples/12-grok.ts +154 -0
- package/examples/13-gemini.ts +48 -0
- package/package.json +14 -3
- package/src/agent/agent.ts +273 -15
- package/src/agent/loop-detector.ts +137 -0
- package/src/agent/pool.ts +9 -2
- package/src/agent/runner.ts +148 -19
- package/src/agent/structured-output.ts +126 -0
- package/src/index.ts +17 -1
- package/src/llm/adapter.ts +29 -5
- package/src/llm/anthropic.ts +2 -1
- package/src/llm/copilot.ts +552 -0
- package/src/llm/gemini.ts +378 -0
- package/src/llm/grok.ts +29 -0
- package/src/llm/openai-common.ts +294 -0
- package/src/llm/openai.ts +31 -261
- package/src/orchestrator/orchestrator.ts +260 -40
- package/src/task/queue.ts +74 -4
- package/src/task/task.ts +8 -1
- package/src/team/messaging.ts +3 -1
- package/src/tool/text-tool-extractor.ts +219 -0
- package/src/types.ts +186 -6
- package/src/utils/trace.ts +34 -0
- package/tests/agent-hooks.test.ts +473 -0
- package/tests/agent-pool.test.ts +212 -0
- package/tests/approval.test.ts +464 -0
- package/tests/built-in-tools.test.ts +393 -0
- package/tests/gemini-adapter.test.ts +97 -0
- package/tests/grok-adapter.test.ts +74 -0
- package/tests/llm-adapters.test.ts +357 -0
- package/tests/loop-detection.test.ts +456 -0
- package/tests/openai-fallback.test.ts +159 -0
- package/tests/orchestrator.test.ts +281 -0
- package/tests/scheduler.test.ts +221 -0
- package/tests/semaphore.test.ts +57 -0
- package/tests/shared-memory.test.ts +122 -0
- package/tests/structured-output.test.ts +331 -0
- package/tests/task-queue.test.ts +244 -0
- package/tests/task-retry.test.ts +368 -0
- package/tests/task-utils.test.ts +155 -0
- package/tests/team-messaging.test.ts +329 -0
- package/tests/text-tool-extractor.test.ts +170 -0
- package/tests/tool-executor.test.ts +193 -0
- package/tests/trace.test.ts +453 -0
- package/vitest.config.ts +9 -0
package/src/llm/openai.ts
CHANGED
|
@@ -32,14 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
import OpenAI from 'openai'
|
|
34
34
|
import type {
|
|
35
|
-
ChatCompletion,
|
|
36
|
-
ChatCompletionAssistantMessageParam,
|
|
37
35
|
ChatCompletionChunk,
|
|
38
|
-
ChatCompletionMessageParam,
|
|
39
|
-
ChatCompletionMessageToolCall,
|
|
40
|
-
ChatCompletionTool,
|
|
41
|
-
ChatCompletionToolMessageParam,
|
|
42
|
-
ChatCompletionUserMessageParam,
|
|
43
36
|
} from 'openai/resources/chat/completions/index.js'
|
|
44
37
|
|
|
45
38
|
import type {
|
|
@@ -55,231 +48,13 @@ import type {
|
|
|
55
48
|
ToolUseBlock,
|
|
56
49
|
} from '../types.js'
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* OpenAI wraps the function definition inside a `function` key and a `type`
|
|
66
|
-
* discriminant. The `inputSchema` is already a JSON Schema object.
|
|
67
|
-
*/
|
|
68
|
-
function toOpenAITool(tool: LLMToolDef): ChatCompletionTool {
|
|
69
|
-
return {
|
|
70
|
-
type: 'function',
|
|
71
|
-
function: {
|
|
72
|
-
name: tool.name,
|
|
73
|
-
description: tool.description,
|
|
74
|
-
parameters: tool.inputSchema as Record<string, unknown>,
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Determine whether a framework message contains any `tool_result` content
|
|
81
|
-
* blocks, which must be serialised as separate OpenAI `tool`-role messages.
|
|
82
|
-
*/
|
|
83
|
-
function hasToolResults(msg: LLMMessage): boolean {
|
|
84
|
-
return msg.content.some((b) => b.type === 'tool_result')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Convert a single framework {@link LLMMessage} into one or more OpenAI
|
|
89
|
-
* {@link ChatCompletionMessageParam} entries.
|
|
90
|
-
*
|
|
91
|
-
* The expansion is necessary because OpenAI represents tool results as
|
|
92
|
-
* top-level messages with role `tool`, whereas in our model they are content
|
|
93
|
-
* blocks inside a `user` message.
|
|
94
|
-
*
|
|
95
|
-
* Expansion rules:
|
|
96
|
-
* - A `user` message containing only text/image blocks → single user message
|
|
97
|
-
* - A `user` message containing `tool_result` blocks → one `tool` message per
|
|
98
|
-
* tool_result block; any remaining text/image blocks are folded into an
|
|
99
|
-
* additional user message prepended to the group
|
|
100
|
-
* - An `assistant` message → single assistant message with optional tool_calls
|
|
101
|
-
*/
|
|
102
|
-
function toOpenAIMessages(messages: LLMMessage[]): ChatCompletionMessageParam[] {
|
|
103
|
-
const result: ChatCompletionMessageParam[] = []
|
|
104
|
-
|
|
105
|
-
for (const msg of messages) {
|
|
106
|
-
if (msg.role === 'assistant') {
|
|
107
|
-
result.push(toOpenAIAssistantMessage(msg))
|
|
108
|
-
} else {
|
|
109
|
-
// user role
|
|
110
|
-
if (!hasToolResults(msg)) {
|
|
111
|
-
result.push(toOpenAIUserMessage(msg))
|
|
112
|
-
} else {
|
|
113
|
-
// Split: text/image blocks become a user message (if any exist), then
|
|
114
|
-
// each tool_result block becomes an independent tool message.
|
|
115
|
-
const nonToolBlocks = msg.content.filter((b) => b.type !== 'tool_result')
|
|
116
|
-
if (nonToolBlocks.length > 0) {
|
|
117
|
-
result.push(toOpenAIUserMessage({ role: 'user', content: nonToolBlocks }))
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
for (const block of msg.content) {
|
|
121
|
-
if (block.type === 'tool_result') {
|
|
122
|
-
const toolMsg: ChatCompletionToolMessageParam = {
|
|
123
|
-
role: 'tool',
|
|
124
|
-
tool_call_id: block.tool_use_id,
|
|
125
|
-
content: block.content,
|
|
126
|
-
}
|
|
127
|
-
result.push(toolMsg)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return result
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Convert a `user`-role framework message into an OpenAI user message.
|
|
139
|
-
* Image blocks are converted to the OpenAI image_url content part format.
|
|
140
|
-
*/
|
|
141
|
-
function toOpenAIUserMessage(msg: LLMMessage): ChatCompletionUserMessageParam {
|
|
142
|
-
// If the entire content is a single text block, use the compact string form
|
|
143
|
-
// to keep the request payload smaller.
|
|
144
|
-
if (msg.content.length === 1 && msg.content[0]?.type === 'text') {
|
|
145
|
-
return { role: 'user', content: msg.content[0].text }
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
type ContentPart = OpenAI.Chat.ChatCompletionContentPartText | OpenAI.Chat.ChatCompletionContentPartImage
|
|
149
|
-
const parts: ContentPart[] = []
|
|
150
|
-
|
|
151
|
-
for (const block of msg.content) {
|
|
152
|
-
if (block.type === 'text') {
|
|
153
|
-
parts.push({ type: 'text', text: block.text })
|
|
154
|
-
} else if (block.type === 'image') {
|
|
155
|
-
parts.push({
|
|
156
|
-
type: 'image_url',
|
|
157
|
-
image_url: {
|
|
158
|
-
url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
159
|
-
},
|
|
160
|
-
})
|
|
161
|
-
}
|
|
162
|
-
// tool_result blocks are handled by the caller (toOpenAIMessages); skip here.
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return { role: 'user', content: parts }
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Convert an `assistant`-role framework message into an OpenAI assistant message.
|
|
170
|
-
*
|
|
171
|
-
* Any `tool_use` blocks become `tool_calls`; `text` blocks become the message content.
|
|
172
|
-
*/
|
|
173
|
-
function toOpenAIAssistantMessage(msg: LLMMessage): ChatCompletionAssistantMessageParam {
|
|
174
|
-
const toolCalls: ChatCompletionMessageToolCall[] = []
|
|
175
|
-
const textParts: string[] = []
|
|
176
|
-
|
|
177
|
-
for (const block of msg.content) {
|
|
178
|
-
if (block.type === 'tool_use') {
|
|
179
|
-
toolCalls.push({
|
|
180
|
-
id: block.id,
|
|
181
|
-
type: 'function',
|
|
182
|
-
function: {
|
|
183
|
-
name: block.name,
|
|
184
|
-
arguments: JSON.stringify(block.input),
|
|
185
|
-
},
|
|
186
|
-
})
|
|
187
|
-
} else if (block.type === 'text') {
|
|
188
|
-
textParts.push(block.text)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const assistantMsg: ChatCompletionAssistantMessageParam = {
|
|
193
|
-
role: 'assistant',
|
|
194
|
-
content: textParts.length > 0 ? textParts.join('') : null,
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (toolCalls.length > 0) {
|
|
198
|
-
assistantMsg.tool_calls = toolCalls
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return assistantMsg
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
// Internal helpers — OpenAI → framework
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Convert an OpenAI {@link ChatCompletion} into a framework {@link LLMResponse}.
|
|
210
|
-
*
|
|
211
|
-
* We take only the first choice (index 0), consistent with how the framework
|
|
212
|
-
* is designed for single-output agents.
|
|
213
|
-
*/
|
|
214
|
-
function fromOpenAICompletion(completion: ChatCompletion): LLMResponse {
|
|
215
|
-
const choice = completion.choices[0]
|
|
216
|
-
if (choice === undefined) {
|
|
217
|
-
throw new Error('OpenAI returned a completion with no choices')
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const content: ContentBlock[] = []
|
|
221
|
-
const message = choice.message
|
|
222
|
-
|
|
223
|
-
if (message.content !== null && message.content !== undefined) {
|
|
224
|
-
const textBlock: TextBlock = { type: 'text', text: message.content }
|
|
225
|
-
content.push(textBlock)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
for (const toolCall of message.tool_calls ?? []) {
|
|
229
|
-
let parsedInput: Record<string, unknown> = {}
|
|
230
|
-
try {
|
|
231
|
-
const parsed: unknown = JSON.parse(toolCall.function.arguments)
|
|
232
|
-
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
233
|
-
parsedInput = parsed as Record<string, unknown>
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
// Malformed arguments from the model — surface as empty object.
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const toolUseBlock: ToolUseBlock = {
|
|
240
|
-
type: 'tool_use',
|
|
241
|
-
id: toolCall.id,
|
|
242
|
-
name: toolCall.function.name,
|
|
243
|
-
input: parsedInput,
|
|
244
|
-
}
|
|
245
|
-
content.push(toolUseBlock)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const stopReason = normalizeFinishReason(choice.finish_reason ?? 'stop')
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
id: completion.id,
|
|
252
|
-
content,
|
|
253
|
-
model: completion.model,
|
|
254
|
-
stop_reason: stopReason,
|
|
255
|
-
usage: {
|
|
256
|
-
input_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
257
|
-
output_tokens: completion.usage?.completion_tokens ?? 0,
|
|
258
|
-
},
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Normalize an OpenAI `finish_reason` string to the framework's canonical
|
|
264
|
-
* stop-reason vocabulary so consumers never need to branch on provider-specific
|
|
265
|
-
* strings.
|
|
266
|
-
*
|
|
267
|
-
* Mapping:
|
|
268
|
-
* - `'stop'` → `'end_turn'`
|
|
269
|
-
* - `'tool_calls'` → `'tool_use'`
|
|
270
|
-
* - `'length'` → `'max_tokens'`
|
|
271
|
-
* - `'content_filter'` → `'content_filter'`
|
|
272
|
-
* - anything else → passed through unchanged
|
|
273
|
-
*/
|
|
274
|
-
function normalizeFinishReason(reason: string): string {
|
|
275
|
-
switch (reason) {
|
|
276
|
-
case 'stop': return 'end_turn'
|
|
277
|
-
case 'tool_calls': return 'tool_use'
|
|
278
|
-
case 'length': return 'max_tokens'
|
|
279
|
-
case 'content_filter': return 'content_filter'
|
|
280
|
-
default: return reason
|
|
281
|
-
}
|
|
282
|
-
}
|
|
51
|
+
import {
|
|
52
|
+
toOpenAITool,
|
|
53
|
+
fromOpenAICompletion,
|
|
54
|
+
normalizeFinishReason,
|
|
55
|
+
buildOpenAIMessageList,
|
|
56
|
+
} from './openai-common.js'
|
|
57
|
+
import { extractToolCallsFromText } from '../tool/text-tool-extractor.js'
|
|
283
58
|
|
|
284
59
|
// ---------------------------------------------------------------------------
|
|
285
60
|
// Adapter implementation
|
|
@@ -291,13 +66,14 @@ function normalizeFinishReason(reason: string): string {
|
|
|
291
66
|
* Thread-safe — a single instance may be shared across concurrent agent runs.
|
|
292
67
|
*/
|
|
293
68
|
export class OpenAIAdapter implements LLMAdapter {
|
|
294
|
-
readonly name = 'openai'
|
|
69
|
+
readonly name: string = 'openai'
|
|
295
70
|
|
|
296
71
|
readonly #client: OpenAI
|
|
297
72
|
|
|
298
|
-
constructor(apiKey?: string) {
|
|
73
|
+
constructor(apiKey?: string, baseURL?: string) {
|
|
299
74
|
this.#client = new OpenAI({
|
|
300
75
|
apiKey: apiKey ?? process.env['OPENAI_API_KEY'],
|
|
76
|
+
baseURL,
|
|
301
77
|
})
|
|
302
78
|
}
|
|
303
79
|
|
|
@@ -329,7 +105,8 @@ export class OpenAIAdapter implements LLMAdapter {
|
|
|
329
105
|
},
|
|
330
106
|
)
|
|
331
107
|
|
|
332
|
-
|
|
108
|
+
const toolNames = options.tools?.map(t => t.name)
|
|
109
|
+
return fromOpenAICompletion(completion, toolNames)
|
|
333
110
|
}
|
|
334
111
|
|
|
335
112
|
// -------------------------------------------------------------------------
|
|
@@ -466,11 +243,29 @@ export class OpenAIAdapter implements LLMAdapter {
|
|
|
466
243
|
}
|
|
467
244
|
doneContent.push(...finalToolUseBlocks)
|
|
468
245
|
|
|
246
|
+
// Fallback: extract tool calls from text when streaming produced no
|
|
247
|
+
// native tool_calls (same logic as fromOpenAICompletion).
|
|
248
|
+
if (finalToolUseBlocks.length === 0 && fullText.length > 0 && options.tools) {
|
|
249
|
+
const toolNames = options.tools.map(t => t.name)
|
|
250
|
+
const extracted = extractToolCallsFromText(fullText, toolNames)
|
|
251
|
+
if (extracted.length > 0) {
|
|
252
|
+
doneContent.push(...extracted)
|
|
253
|
+
for (const block of extracted) {
|
|
254
|
+
yield { type: 'tool_use', data: block } satisfies StreamEvent
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const hasToolUseBlocks = doneContent.some(b => b.type === 'tool_use')
|
|
260
|
+
const resolvedStopReason = hasToolUseBlocks && finalFinishReason === 'stop'
|
|
261
|
+
? 'tool_use'
|
|
262
|
+
: normalizeFinishReason(finalFinishReason)
|
|
263
|
+
|
|
469
264
|
const finalResponse: LLMResponse = {
|
|
470
265
|
id: completionId,
|
|
471
266
|
content: doneContent,
|
|
472
267
|
model: completionModel,
|
|
473
|
-
stop_reason:
|
|
268
|
+
stop_reason: resolvedStopReason,
|
|
474
269
|
usage: { input_tokens: inputTokens, output_tokens: outputTokens },
|
|
475
270
|
}
|
|
476
271
|
|
|
@@ -484,31 +279,6 @@ export class OpenAIAdapter implements LLMAdapter {
|
|
|
484
279
|
}
|
|
485
280
|
}
|
|
486
281
|
|
|
487
|
-
// ---------------------------------------------------------------------------
|
|
488
|
-
// Private utility
|
|
489
|
-
// ---------------------------------------------------------------------------
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Prepend a system message when `systemPrompt` is provided, then append the
|
|
493
|
-
* converted conversation messages.
|
|
494
|
-
*
|
|
495
|
-
* OpenAI represents system instructions as a message with `role: 'system'`
|
|
496
|
-
* at the top of the array, not as a separate API parameter.
|
|
497
|
-
*/
|
|
498
|
-
function buildOpenAIMessageList(
|
|
499
|
-
messages: LLMMessage[],
|
|
500
|
-
systemPrompt: string | undefined,
|
|
501
|
-
): ChatCompletionMessageParam[] {
|
|
502
|
-
const result: ChatCompletionMessageParam[] = []
|
|
503
|
-
|
|
504
|
-
if (systemPrompt !== undefined && systemPrompt.length > 0) {
|
|
505
|
-
result.push({ role: 'system', content: systemPrompt })
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
result.push(...toOpenAIMessages(messages))
|
|
509
|
-
return result
|
|
510
|
-
}
|
|
511
|
-
|
|
512
282
|
// Re-export types that consumers of this module commonly need alongside the adapter.
|
|
513
283
|
export type {
|
|
514
284
|
ContentBlock,
|