mu-core 0.33.0 → 0.34.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/{esm/types.d.ts → dist/index.d.ts} +55 -11
- package/dist/index.js +104 -0
- package/package.json +13 -18
- package/esm/agent.d.ts +0 -42
- package/esm/agent.js +0 -104
- package/esm/index.d.ts +0 -4
- package/esm/index.js +0 -2
- package/esm/package.json +0 -3
- package/esm/types.js +0 -3
- package/script/agent.d.ts +0 -42
- package/script/agent.js +0 -109
- package/script/index.d.ts +0 -4
- package/script/index.js +0 -10
- package/script/package.json +0 -3
- package/script/types.d.ts +0 -78
- package/script/types.js +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type ContentPart = {
|
|
2
2
|
type: 'text';
|
|
3
3
|
text: string;
|
|
4
4
|
} | {
|
|
@@ -19,15 +19,15 @@ export type ContentPart = {
|
|
|
19
19
|
id: string;
|
|
20
20
|
content: ContentPart[];
|
|
21
21
|
};
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
type Role = 'system' | 'user' | 'assistant' | 'tool';
|
|
23
|
+
type Message = {
|
|
24
24
|
role: Role;
|
|
25
25
|
content: ContentPart[];
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
declare const text: (value: string) => ContentPart;
|
|
28
|
+
declare const image: (mime: string, data: Uint8Array) => ContentPart;
|
|
29
|
+
declare const audio: (mime: string, data: Uint8Array) => ContentPart;
|
|
30
|
+
interface Tool {
|
|
31
31
|
name: string;
|
|
32
32
|
description: string;
|
|
33
33
|
parameters: Record<string, unknown>;
|
|
@@ -36,13 +36,13 @@ export interface Tool {
|
|
|
36
36
|
signal?: AbortSignal;
|
|
37
37
|
}): Promise<ContentPart[]>;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
interface Usage {
|
|
40
40
|
input?: number;
|
|
41
41
|
output?: number;
|
|
42
42
|
total?: number;
|
|
43
43
|
contextWindow?: number;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
type StreamEvent = ContentPart | {
|
|
46
46
|
type: 'usage';
|
|
47
47
|
usage: Usage;
|
|
48
48
|
} | {
|
|
@@ -50,11 +50,11 @@ export type StreamEvent = ContentPart | {
|
|
|
50
50
|
text: string;
|
|
51
51
|
};
|
|
52
52
|
/** Non-text input modalities a model accepts. */
|
|
53
|
-
|
|
53
|
+
interface ModelModalities {
|
|
54
54
|
vision: boolean;
|
|
55
55
|
audio: boolean;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
interface Provider {
|
|
58
58
|
stream(req: {
|
|
59
59
|
model: string;
|
|
60
60
|
messages: Message[];
|
|
@@ -76,3 +76,47 @@ export interface Provider {
|
|
|
76
76
|
/** The model's context window in tokens, when the provider can report it. */
|
|
77
77
|
contextWindow?(model: string): Promise<number | undefined>;
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
type LoopEvent = ContentPart | {
|
|
81
|
+
type: 'usage';
|
|
82
|
+
usage: Usage;
|
|
83
|
+
} | {
|
|
84
|
+
type: 'reasoning';
|
|
85
|
+
text: string;
|
|
86
|
+
} | {
|
|
87
|
+
type: 'message';
|
|
88
|
+
message: Message;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'error';
|
|
91
|
+
error: Error;
|
|
92
|
+
} | {
|
|
93
|
+
type: 'done';
|
|
94
|
+
messages: Message[];
|
|
95
|
+
};
|
|
96
|
+
interface RunOptions {
|
|
97
|
+
provider: Provider;
|
|
98
|
+
model: string;
|
|
99
|
+
messages: Message[];
|
|
100
|
+
tools?: Tool[];
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
}
|
|
103
|
+
declare function run(opts: RunOptions): AsyncIterable<LoopEvent>;
|
|
104
|
+
interface AgentConfig {
|
|
105
|
+
provider: Provider;
|
|
106
|
+
model: string;
|
|
107
|
+
tools?: Tool[];
|
|
108
|
+
system?: string;
|
|
109
|
+
signal?: AbortSignal;
|
|
110
|
+
}
|
|
111
|
+
type Input = string | ContentPart[] | Message[];
|
|
112
|
+
interface AgentResult {
|
|
113
|
+
message: Message;
|
|
114
|
+
messages: Message[];
|
|
115
|
+
}
|
|
116
|
+
interface Agent {
|
|
117
|
+
stream(input: Input): AsyncIterable<LoopEvent>;
|
|
118
|
+
run(input: Input): Promise<AgentResult>;
|
|
119
|
+
}
|
|
120
|
+
declare const createAgent: (config: AgentConfig) => Agent;
|
|
121
|
+
|
|
122
|
+
export { type Agent, type AgentConfig, type AgentResult, type ContentPart, type Input, type LoopEvent, type Message, type ModelModalities, type Provider, type Role, type RunOptions, type StreamEvent, type Tool, type Usage, audio, createAgent, image, run, text };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var text = (value) => ({ type: "text", text: value });
|
|
3
|
+
var image = (mime, data) => ({ type: "image", mime, data });
|
|
4
|
+
var audio = (mime, data) => ({ type: "audio", mime, data });
|
|
5
|
+
|
|
6
|
+
// src/agent.ts
|
|
7
|
+
var append = (parts, part) => {
|
|
8
|
+
const last = parts[parts.length - 1];
|
|
9
|
+
if (part.type === "text" && last?.type === "text") {
|
|
10
|
+
last.text += part.text;
|
|
11
|
+
} else if (part.type === "audio" && last?.type === "audio" && last.mime === part.mime) {
|
|
12
|
+
const merged = new Uint8Array(last.data.length + part.data.length);
|
|
13
|
+
merged.set(last.data);
|
|
14
|
+
merged.set(part.data, last.data.length);
|
|
15
|
+
last.data = merged;
|
|
16
|
+
} else if (part.type === "text") {
|
|
17
|
+
parts.push({ type: "text", text: part.text });
|
|
18
|
+
} else if (part.type === "audio") {
|
|
19
|
+
parts.push({ type: "audio", mime: part.mime, data: part.data });
|
|
20
|
+
} else {
|
|
21
|
+
parts.push(part);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var execute = async (tools, call, signal) => {
|
|
25
|
+
const tool = tools.get(call.name);
|
|
26
|
+
if (!tool) return [{ type: "text", text: `Unknown tool: ${call.name}` }];
|
|
27
|
+
try {
|
|
28
|
+
return await tool.run(call.input, { signal });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return [{ type: "text", text: err instanceof Error ? err.message : String(err) }];
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
async function* run(opts) {
|
|
34
|
+
const { provider, model, signal } = opts;
|
|
35
|
+
const tools = opts.tools ?? [];
|
|
36
|
+
const registry = new Map(tools.map((t) => [t.name, t]));
|
|
37
|
+
const messages = [...opts.messages];
|
|
38
|
+
while (true) {
|
|
39
|
+
if (signal?.aborted) break;
|
|
40
|
+
const content = [];
|
|
41
|
+
const calls = [];
|
|
42
|
+
try {
|
|
43
|
+
for await (const event of provider.stream({ model, messages, tools, signal })) {
|
|
44
|
+
if (event.type === "usage" || event.type === "reasoning") {
|
|
45
|
+
yield event;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
yield event;
|
|
49
|
+
append(content, event);
|
|
50
|
+
if (event.type === "tool_call") calls.push(event);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
yield { type: "error", error: error instanceof Error ? error : new Error(String(error)) };
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
const message = { role: "assistant", content };
|
|
57
|
+
messages.push(message);
|
|
58
|
+
yield { type: "message", message };
|
|
59
|
+
if (calls.length === 0) break;
|
|
60
|
+
const results = await Promise.all(
|
|
61
|
+
calls.map(async (call) => ({
|
|
62
|
+
type: "tool_result",
|
|
63
|
+
id: call.id,
|
|
64
|
+
content: await execute(registry, call, signal)
|
|
65
|
+
}))
|
|
66
|
+
);
|
|
67
|
+
const toolMessage = { role: "tool", content: results };
|
|
68
|
+
messages.push(toolMessage);
|
|
69
|
+
yield { type: "message", message: toolMessage };
|
|
70
|
+
}
|
|
71
|
+
yield { type: "done", messages };
|
|
72
|
+
}
|
|
73
|
+
var isMessages = (input) => input.length > 0 && "role" in input[0];
|
|
74
|
+
var toMessages = (input) => {
|
|
75
|
+
if (typeof input === "string") return [{ role: "user", content: [{ type: "text", text: input }] }];
|
|
76
|
+
if (isMessages(input)) return input;
|
|
77
|
+
return [{ role: "user", content: input }];
|
|
78
|
+
};
|
|
79
|
+
var createAgent = (config) => {
|
|
80
|
+
const tools = config.tools ?? [];
|
|
81
|
+
const build = (input) => {
|
|
82
|
+
const messages = toMessages(input);
|
|
83
|
+
if (!config.system) return messages;
|
|
84
|
+
return [{ role: "system", content: [{ type: "text", text: config.system }] }, ...messages];
|
|
85
|
+
};
|
|
86
|
+
const stream = (input) => run({ provider: config.provider, model: config.model, tools, messages: build(input), signal: config.signal });
|
|
87
|
+
const runToEnd = async (input) => {
|
|
88
|
+
let message = { role: "assistant", content: [] };
|
|
89
|
+
let messages = [];
|
|
90
|
+
for await (const event of stream(input)) {
|
|
91
|
+
if (event.type === "message" && event.message.role === "assistant") message = event.message;
|
|
92
|
+
else if (event.type === "done") messages = event.messages;
|
|
93
|
+
}
|
|
94
|
+
return { message, messages };
|
|
95
|
+
};
|
|
96
|
+
return { stream, run: runToEnd };
|
|
97
|
+
};
|
|
98
|
+
export {
|
|
99
|
+
audio,
|
|
100
|
+
createAgent,
|
|
101
|
+
image,
|
|
102
|
+
run,
|
|
103
|
+
text
|
|
104
|
+
};
|
package/package.json
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mu-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "Standalone multimodal agentic loop: content, messages, tools, provider interface, createAgent",
|
|
5
|
-
"
|
|
6
|
-
"main": "./
|
|
7
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"default": "./esm/index.js"
|
|
13
|
-
},
|
|
14
|
-
"require": {
|
|
15
|
-
"types": "./script/index.d.ts",
|
|
16
|
-
"default": "./script/index.js"
|
|
17
|
-
}
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
18
12
|
}
|
|
19
13
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
}
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/esm/agent.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { ContentPart, Message, Provider, Tool, Usage } from './types.js';
|
|
2
|
-
export type LoopEvent = ContentPart | {
|
|
3
|
-
type: 'usage';
|
|
4
|
-
usage: Usage;
|
|
5
|
-
} | {
|
|
6
|
-
type: 'reasoning';
|
|
7
|
-
text: string;
|
|
8
|
-
} | {
|
|
9
|
-
type: 'message';
|
|
10
|
-
message: Message;
|
|
11
|
-
} | {
|
|
12
|
-
type: 'error';
|
|
13
|
-
error: Error;
|
|
14
|
-
} | {
|
|
15
|
-
type: 'done';
|
|
16
|
-
messages: Message[];
|
|
17
|
-
};
|
|
18
|
-
export interface RunOptions {
|
|
19
|
-
provider: Provider;
|
|
20
|
-
model: string;
|
|
21
|
-
messages: Message[];
|
|
22
|
-
tools?: Tool[];
|
|
23
|
-
signal?: AbortSignal;
|
|
24
|
-
}
|
|
25
|
-
export declare function run(opts: RunOptions): AsyncIterable<LoopEvent>;
|
|
26
|
-
export interface AgentConfig {
|
|
27
|
-
provider: Provider;
|
|
28
|
-
model: string;
|
|
29
|
-
tools?: Tool[];
|
|
30
|
-
system?: string;
|
|
31
|
-
signal?: AbortSignal;
|
|
32
|
-
}
|
|
33
|
-
export type Input = string | ContentPart[] | Message[];
|
|
34
|
-
export interface AgentResult {
|
|
35
|
-
message: Message;
|
|
36
|
-
messages: Message[];
|
|
37
|
-
}
|
|
38
|
-
export interface Agent {
|
|
39
|
-
stream(input: Input): AsyncIterable<LoopEvent>;
|
|
40
|
-
run(input: Input): Promise<AgentResult>;
|
|
41
|
-
}
|
|
42
|
-
export declare const createAgent: (config: AgentConfig) => Agent;
|
package/esm/agent.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
const append = (parts, part) => {
|
|
2
|
-
const last = parts[parts.length - 1];
|
|
3
|
-
if (part.type === 'text' && last?.type === 'text') {
|
|
4
|
-
last.text += part.text;
|
|
5
|
-
}
|
|
6
|
-
else if (part.type === 'audio' && last?.type === 'audio' && last.mime === part.mime) {
|
|
7
|
-
const merged = new Uint8Array(last.data.length + part.data.length);
|
|
8
|
-
merged.set(last.data);
|
|
9
|
-
merged.set(part.data, last.data.length);
|
|
10
|
-
last.data = merged;
|
|
11
|
-
}
|
|
12
|
-
else if (part.type === 'text') {
|
|
13
|
-
parts.push({ type: 'text', text: part.text });
|
|
14
|
-
}
|
|
15
|
-
else if (part.type === 'audio') {
|
|
16
|
-
parts.push({ type: 'audio', mime: part.mime, data: part.data });
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
parts.push(part);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const execute = async (tools, call, signal) => {
|
|
23
|
-
const tool = tools.get(call.name);
|
|
24
|
-
if (!tool)
|
|
25
|
-
return [{ type: 'text', text: `Unknown tool: ${call.name}` }];
|
|
26
|
-
try {
|
|
27
|
-
return await tool.run(call.input, { signal });
|
|
28
|
-
}
|
|
29
|
-
catch (err) {
|
|
30
|
-
return [{ type: 'text', text: err instanceof Error ? err.message : String(err) }];
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
export async function* run(opts) {
|
|
34
|
-
const { provider, model, signal } = opts;
|
|
35
|
-
const tools = opts.tools ?? [];
|
|
36
|
-
const registry = new Map(tools.map((t) => [t.name, t]));
|
|
37
|
-
const messages = [...opts.messages];
|
|
38
|
-
while (true) {
|
|
39
|
-
if (signal?.aborted)
|
|
40
|
-
break;
|
|
41
|
-
const content = [];
|
|
42
|
-
const calls = [];
|
|
43
|
-
try {
|
|
44
|
-
for await (const event of provider.stream({ model, messages, tools, signal })) {
|
|
45
|
-
if (event.type === 'usage' || event.type === 'reasoning') {
|
|
46
|
-
yield event;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
yield event;
|
|
50
|
-
append(content, event);
|
|
51
|
-
if (event.type === 'tool_call')
|
|
52
|
-
calls.push(event);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
yield { type: 'error', error: error instanceof Error ? error : new Error(String(error)) };
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
const message = { role: 'assistant', content };
|
|
60
|
-
messages.push(message);
|
|
61
|
-
yield { type: 'message', message };
|
|
62
|
-
if (calls.length === 0)
|
|
63
|
-
break;
|
|
64
|
-
const results = await Promise.all(calls.map(async (call) => ({
|
|
65
|
-
type: 'tool_result',
|
|
66
|
-
id: call.id,
|
|
67
|
-
content: await execute(registry, call, signal),
|
|
68
|
-
})));
|
|
69
|
-
const toolMessage = { role: 'tool', content: results };
|
|
70
|
-
messages.push(toolMessage);
|
|
71
|
-
yield { type: 'message', message: toolMessage };
|
|
72
|
-
}
|
|
73
|
-
yield { type: 'done', messages };
|
|
74
|
-
}
|
|
75
|
-
const isMessages = (input) => input.length > 0 && 'role' in input[0];
|
|
76
|
-
const toMessages = (input) => {
|
|
77
|
-
if (typeof input === 'string')
|
|
78
|
-
return [{ role: 'user', content: [{ type: 'text', text: input }] }];
|
|
79
|
-
if (isMessages(input))
|
|
80
|
-
return input;
|
|
81
|
-
return [{ role: 'user', content: input }];
|
|
82
|
-
};
|
|
83
|
-
export const createAgent = (config) => {
|
|
84
|
-
const tools = config.tools ?? [];
|
|
85
|
-
const build = (input) => {
|
|
86
|
-
const messages = toMessages(input);
|
|
87
|
-
if (!config.system)
|
|
88
|
-
return messages;
|
|
89
|
-
return [{ role: 'system', content: [{ type: 'text', text: config.system }] }, ...messages];
|
|
90
|
-
};
|
|
91
|
-
const stream = (input) => run({ provider: config.provider, model: config.model, tools, messages: build(input), signal: config.signal });
|
|
92
|
-
const runToEnd = async (input) => {
|
|
93
|
-
let message = { role: 'assistant', content: [] };
|
|
94
|
-
let messages = [];
|
|
95
|
-
for await (const event of stream(input)) {
|
|
96
|
-
if (event.type === 'message' && event.message.role === 'assistant')
|
|
97
|
-
message = event.message;
|
|
98
|
-
else if (event.type === 'done')
|
|
99
|
-
messages = event.messages;
|
|
100
|
-
}
|
|
101
|
-
return { message, messages };
|
|
102
|
-
};
|
|
103
|
-
return { stream, run: runToEnd };
|
|
104
|
-
};
|
package/esm/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type { ContentPart, Message, ModelModalities, Provider, Role, StreamEvent, Tool, Usage } from './types.js';
|
|
2
|
-
export { audio, image, text } from './types.js';
|
|
3
|
-
export type { Agent, AgentConfig, AgentResult, Input, LoopEvent, RunOptions } from './agent.js';
|
|
4
|
-
export { createAgent, run } from './agent.js';
|
package/esm/index.js
DELETED
package/esm/package.json
DELETED
package/esm/types.js
DELETED
package/script/agent.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { ContentPart, Message, Provider, Tool, Usage } from './types.js';
|
|
2
|
-
export type LoopEvent = ContentPart | {
|
|
3
|
-
type: 'usage';
|
|
4
|
-
usage: Usage;
|
|
5
|
-
} | {
|
|
6
|
-
type: 'reasoning';
|
|
7
|
-
text: string;
|
|
8
|
-
} | {
|
|
9
|
-
type: 'message';
|
|
10
|
-
message: Message;
|
|
11
|
-
} | {
|
|
12
|
-
type: 'error';
|
|
13
|
-
error: Error;
|
|
14
|
-
} | {
|
|
15
|
-
type: 'done';
|
|
16
|
-
messages: Message[];
|
|
17
|
-
};
|
|
18
|
-
export interface RunOptions {
|
|
19
|
-
provider: Provider;
|
|
20
|
-
model: string;
|
|
21
|
-
messages: Message[];
|
|
22
|
-
tools?: Tool[];
|
|
23
|
-
signal?: AbortSignal;
|
|
24
|
-
}
|
|
25
|
-
export declare function run(opts: RunOptions): AsyncIterable<LoopEvent>;
|
|
26
|
-
export interface AgentConfig {
|
|
27
|
-
provider: Provider;
|
|
28
|
-
model: string;
|
|
29
|
-
tools?: Tool[];
|
|
30
|
-
system?: string;
|
|
31
|
-
signal?: AbortSignal;
|
|
32
|
-
}
|
|
33
|
-
export type Input = string | ContentPart[] | Message[];
|
|
34
|
-
export interface AgentResult {
|
|
35
|
-
message: Message;
|
|
36
|
-
messages: Message[];
|
|
37
|
-
}
|
|
38
|
-
export interface Agent {
|
|
39
|
-
stream(input: Input): AsyncIterable<LoopEvent>;
|
|
40
|
-
run(input: Input): Promise<AgentResult>;
|
|
41
|
-
}
|
|
42
|
-
export declare const createAgent: (config: AgentConfig) => Agent;
|
package/script/agent.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAgent = void 0;
|
|
4
|
-
exports.run = run;
|
|
5
|
-
const append = (parts, part) => {
|
|
6
|
-
const last = parts[parts.length - 1];
|
|
7
|
-
if (part.type === 'text' && last?.type === 'text') {
|
|
8
|
-
last.text += part.text;
|
|
9
|
-
}
|
|
10
|
-
else if (part.type === 'audio' && last?.type === 'audio' && last.mime === part.mime) {
|
|
11
|
-
const merged = new Uint8Array(last.data.length + part.data.length);
|
|
12
|
-
merged.set(last.data);
|
|
13
|
-
merged.set(part.data, last.data.length);
|
|
14
|
-
last.data = merged;
|
|
15
|
-
}
|
|
16
|
-
else if (part.type === 'text') {
|
|
17
|
-
parts.push({ type: 'text', text: part.text });
|
|
18
|
-
}
|
|
19
|
-
else if (part.type === 'audio') {
|
|
20
|
-
parts.push({ type: 'audio', mime: part.mime, data: part.data });
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
parts.push(part);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
const execute = async (tools, call, signal) => {
|
|
27
|
-
const tool = tools.get(call.name);
|
|
28
|
-
if (!tool)
|
|
29
|
-
return [{ type: 'text', text: `Unknown tool: ${call.name}` }];
|
|
30
|
-
try {
|
|
31
|
-
return await tool.run(call.input, { signal });
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
return [{ type: 'text', text: err instanceof Error ? err.message : String(err) }];
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
async function* run(opts) {
|
|
38
|
-
const { provider, model, signal } = opts;
|
|
39
|
-
const tools = opts.tools ?? [];
|
|
40
|
-
const registry = new Map(tools.map((t) => [t.name, t]));
|
|
41
|
-
const messages = [...opts.messages];
|
|
42
|
-
while (true) {
|
|
43
|
-
if (signal?.aborted)
|
|
44
|
-
break;
|
|
45
|
-
const content = [];
|
|
46
|
-
const calls = [];
|
|
47
|
-
try {
|
|
48
|
-
for await (const event of provider.stream({ model, messages, tools, signal })) {
|
|
49
|
-
if (event.type === 'usage' || event.type === 'reasoning') {
|
|
50
|
-
yield event;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
yield event;
|
|
54
|
-
append(content, event);
|
|
55
|
-
if (event.type === 'tool_call')
|
|
56
|
-
calls.push(event);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
yield { type: 'error', error: error instanceof Error ? error : new Error(String(error)) };
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
const message = { role: 'assistant', content };
|
|
64
|
-
messages.push(message);
|
|
65
|
-
yield { type: 'message', message };
|
|
66
|
-
if (calls.length === 0)
|
|
67
|
-
break;
|
|
68
|
-
const results = await Promise.all(calls.map(async (call) => ({
|
|
69
|
-
type: 'tool_result',
|
|
70
|
-
id: call.id,
|
|
71
|
-
content: await execute(registry, call, signal),
|
|
72
|
-
})));
|
|
73
|
-
const toolMessage = { role: 'tool', content: results };
|
|
74
|
-
messages.push(toolMessage);
|
|
75
|
-
yield { type: 'message', message: toolMessage };
|
|
76
|
-
}
|
|
77
|
-
yield { type: 'done', messages };
|
|
78
|
-
}
|
|
79
|
-
const isMessages = (input) => input.length > 0 && 'role' in input[0];
|
|
80
|
-
const toMessages = (input) => {
|
|
81
|
-
if (typeof input === 'string')
|
|
82
|
-
return [{ role: 'user', content: [{ type: 'text', text: input }] }];
|
|
83
|
-
if (isMessages(input))
|
|
84
|
-
return input;
|
|
85
|
-
return [{ role: 'user', content: input }];
|
|
86
|
-
};
|
|
87
|
-
const createAgent = (config) => {
|
|
88
|
-
const tools = config.tools ?? [];
|
|
89
|
-
const build = (input) => {
|
|
90
|
-
const messages = toMessages(input);
|
|
91
|
-
if (!config.system)
|
|
92
|
-
return messages;
|
|
93
|
-
return [{ role: 'system', content: [{ type: 'text', text: config.system }] }, ...messages];
|
|
94
|
-
};
|
|
95
|
-
const stream = (input) => run({ provider: config.provider, model: config.model, tools, messages: build(input), signal: config.signal });
|
|
96
|
-
const runToEnd = async (input) => {
|
|
97
|
-
let message = { role: 'assistant', content: [] };
|
|
98
|
-
let messages = [];
|
|
99
|
-
for await (const event of stream(input)) {
|
|
100
|
-
if (event.type === 'message' && event.message.role === 'assistant')
|
|
101
|
-
message = event.message;
|
|
102
|
-
else if (event.type === 'done')
|
|
103
|
-
messages = event.messages;
|
|
104
|
-
}
|
|
105
|
-
return { message, messages };
|
|
106
|
-
};
|
|
107
|
-
return { stream, run: runToEnd };
|
|
108
|
-
};
|
|
109
|
-
exports.createAgent = createAgent;
|
package/script/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type { ContentPart, Message, ModelModalities, Provider, Role, StreamEvent, Tool, Usage } from './types.js';
|
|
2
|
-
export { audio, image, text } from './types.js';
|
|
3
|
-
export type { Agent, AgentConfig, AgentResult, Input, LoopEvent, RunOptions } from './agent.js';
|
|
4
|
-
export { createAgent, run } from './agent.js';
|
package/script/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.run = exports.createAgent = exports.text = exports.image = exports.audio = void 0;
|
|
4
|
-
var types_js_1 = require("./types.js");
|
|
5
|
-
Object.defineProperty(exports, "audio", { enumerable: true, get: function () { return types_js_1.audio; } });
|
|
6
|
-
Object.defineProperty(exports, "image", { enumerable: true, get: function () { return types_js_1.image; } });
|
|
7
|
-
Object.defineProperty(exports, "text", { enumerable: true, get: function () { return types_js_1.text; } });
|
|
8
|
-
var agent_js_1 = require("./agent.js");
|
|
9
|
-
Object.defineProperty(exports, "createAgent", { enumerable: true, get: function () { return agent_js_1.createAgent; } });
|
|
10
|
-
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return agent_js_1.run; } });
|
package/script/package.json
DELETED
package/script/types.d.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
export type ContentPart = {
|
|
2
|
-
type: 'text';
|
|
3
|
-
text: string;
|
|
4
|
-
} | {
|
|
5
|
-
type: 'image';
|
|
6
|
-
mime: string;
|
|
7
|
-
data: Uint8Array;
|
|
8
|
-
} | {
|
|
9
|
-
type: 'audio';
|
|
10
|
-
mime: string;
|
|
11
|
-
data: Uint8Array;
|
|
12
|
-
} | {
|
|
13
|
-
type: 'tool_call';
|
|
14
|
-
id: string;
|
|
15
|
-
name: string;
|
|
16
|
-
input: unknown;
|
|
17
|
-
} | {
|
|
18
|
-
type: 'tool_result';
|
|
19
|
-
id: string;
|
|
20
|
-
content: ContentPart[];
|
|
21
|
-
};
|
|
22
|
-
export type Role = 'system' | 'user' | 'assistant' | 'tool';
|
|
23
|
-
export type Message = {
|
|
24
|
-
role: Role;
|
|
25
|
-
content: ContentPart[];
|
|
26
|
-
};
|
|
27
|
-
export declare const text: (value: string) => ContentPart;
|
|
28
|
-
export declare const image: (mime: string, data: Uint8Array) => ContentPart;
|
|
29
|
-
export declare const audio: (mime: string, data: Uint8Array) => ContentPart;
|
|
30
|
-
export interface Tool {
|
|
31
|
-
name: string;
|
|
32
|
-
description: string;
|
|
33
|
-
parameters: Record<string, unknown>;
|
|
34
|
-
prompt?: string;
|
|
35
|
-
run(input: unknown, ctx: {
|
|
36
|
-
signal?: AbortSignal;
|
|
37
|
-
}): Promise<ContentPart[]>;
|
|
38
|
-
}
|
|
39
|
-
export interface Usage {
|
|
40
|
-
input?: number;
|
|
41
|
-
output?: number;
|
|
42
|
-
total?: number;
|
|
43
|
-
contextWindow?: number;
|
|
44
|
-
}
|
|
45
|
-
export type StreamEvent = ContentPart | {
|
|
46
|
-
type: 'usage';
|
|
47
|
-
usage: Usage;
|
|
48
|
-
} | {
|
|
49
|
-
type: 'reasoning';
|
|
50
|
-
text: string;
|
|
51
|
-
};
|
|
52
|
-
/** Non-text input modalities a model accepts. */
|
|
53
|
-
export interface ModelModalities {
|
|
54
|
-
vision: boolean;
|
|
55
|
-
audio: boolean;
|
|
56
|
-
}
|
|
57
|
-
export interface Provider {
|
|
58
|
-
stream(req: {
|
|
59
|
-
model: string;
|
|
60
|
-
messages: Message[];
|
|
61
|
-
tools: Tool[];
|
|
62
|
-
signal?: AbortSignal;
|
|
63
|
-
/** Per-turn extra chat-template kwargs (e.g. `{ enable_thinking: false }`). A
|
|
64
|
-
* provider wrapper can set this for a single turn; merged over any provider-level
|
|
65
|
-
* default. Providers that don't support it ignore it. */
|
|
66
|
-
chatTemplateKwargs?: Record<string, unknown>;
|
|
67
|
-
}): AsyncIterable<StreamEvent>;
|
|
68
|
-
/**
|
|
69
|
-
* Probe a model's input modalities. MAY load the model (e.g. a llama.cpp `/props`
|
|
70
|
-
* fetch) — call it on model selection, not on every turn. Optional: providers that
|
|
71
|
-
* can't introspect modalities simply omit it.
|
|
72
|
-
*/
|
|
73
|
-
capabilities?(model: string): Promise<ModelModalities | undefined>;
|
|
74
|
-
/** Exact token count of `text` via the model's own tokenizer, when the provider supports it. */
|
|
75
|
-
countTokens?(text: string, model: string): Promise<number | undefined>;
|
|
76
|
-
/** The model's context window in tokens, when the provider can report it. */
|
|
77
|
-
contextWindow?(model: string): Promise<number | undefined>;
|
|
78
|
-
}
|
package/script/types.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.audio = exports.image = exports.text = void 0;
|
|
4
|
-
const text = (value) => ({ type: 'text', text: value });
|
|
5
|
-
exports.text = text;
|
|
6
|
-
const image = (mime, data) => ({ type: 'image', mime, data });
|
|
7
|
-
exports.image = image;
|
|
8
|
-
const audio = (mime, data) => ({ type: 'audio', mime, data });
|
|
9
|
-
exports.audio = audio;
|