@tuttiai/core 0.1.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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +355 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tutti AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @tuttiai/core
|
|
2
|
+
|
|
3
|
+
The runtime engine for [Tutti](https://tutti-ai.com) — an open-source multi-agent orchestration framework for TypeScript.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @tuttiai/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { TuttiRuntime, AnthropicProvider, defineScore } from "@tuttiai/core";
|
|
15
|
+
|
|
16
|
+
const score = defineScore({
|
|
17
|
+
name: "my-project",
|
|
18
|
+
provider: new AnthropicProvider(), // uses ANTHROPIC_API_KEY env var
|
|
19
|
+
agents: {
|
|
20
|
+
assistant: {
|
|
21
|
+
name: "assistant",
|
|
22
|
+
model: "claude-sonnet-4-20250514",
|
|
23
|
+
system_prompt: "You are a helpful assistant.",
|
|
24
|
+
voices: [],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const tutti = new TuttiRuntime(score);
|
|
30
|
+
const result = await tutti.run("assistant", "Hello!");
|
|
31
|
+
console.log(result.output);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## What's included
|
|
35
|
+
|
|
36
|
+
- **TuttiRuntime** — top-level orchestrator
|
|
37
|
+
- **AgentRunner** — agentic while-loop (LLM call → tool execution → repeat)
|
|
38
|
+
- **AnthropicProvider** — `LLMProvider` implementation via `@anthropic-ai/sdk`
|
|
39
|
+
- **EventBus** — typed pub/sub for full lifecycle observability
|
|
40
|
+
- **InMemorySessionStore** — conversation persistence
|
|
41
|
+
- **ScoreLoader** — dynamic import of `tutti.score.ts` files
|
|
42
|
+
- **defineScore()** — typed identity function for score authoring
|
|
43
|
+
|
|
44
|
+
## Observability
|
|
45
|
+
|
|
46
|
+
Every action emits typed events:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
tutti.events.on("tool:start", (e) => {
|
|
50
|
+
console.log(`Calling tool: ${e.tool_name}`);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Links
|
|
55
|
+
|
|
56
|
+
- [Tutti](https://tutti-ai.com)
|
|
57
|
+
- [GitHub](https://github.com/tuttiai/tutti/tree/main/packages/core)
|
|
58
|
+
- [Docs](https://tutti-ai.com/docs)
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { TuttiEventType, TuttiEvent, ScoreConfig, AgentResult, Session, LLMProvider, SessionStore, AgentConfig, ChatMessage, ChatRequest, ChatResponse } from '@tuttiai/types';
|
|
2
|
+
export { AgentConfig, AgentResult, ChatMessage, ChatRequest, ChatResponse, ContentBlock, LLMProvider, ScoreConfig, Session, SessionStore, StopReason, TextBlock, TokenUsage, Tool, ToolContext, ToolDefinition, ToolResult, ToolResultBlock, ToolUseBlock, TuttiEvent, TuttiEventHandler, TuttiEventType, Voice, VoiceContext } from '@tuttiai/types';
|
|
3
|
+
|
|
4
|
+
declare class EventBus {
|
|
5
|
+
private listeners;
|
|
6
|
+
on<T extends TuttiEventType>(type: T, handler: (event: Extract<TuttiEvent, {
|
|
7
|
+
type: T;
|
|
8
|
+
}>) => void): () => void;
|
|
9
|
+
off<T extends TuttiEventType>(type: T, handler: (event: Extract<TuttiEvent, {
|
|
10
|
+
type: T;
|
|
11
|
+
}>) => void): void;
|
|
12
|
+
emit(event: TuttiEvent): void;
|
|
13
|
+
/** Subscribe to all events. */
|
|
14
|
+
onAny(handler: (event: TuttiEvent) => void): () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare class TuttiRuntime {
|
|
18
|
+
readonly events: EventBus;
|
|
19
|
+
private sessions;
|
|
20
|
+
private runner;
|
|
21
|
+
private score;
|
|
22
|
+
constructor(score: ScoreConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Run an agent by name with the given user input.
|
|
25
|
+
* Optionally pass a session_id to continue a conversation.
|
|
26
|
+
*/
|
|
27
|
+
run(agent_name: string, input: string, session_id?: string): Promise<AgentResult>;
|
|
28
|
+
/** Retrieve an existing session. */
|
|
29
|
+
getSession(id: string): Session | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare class AgentRunner {
|
|
33
|
+
private provider;
|
|
34
|
+
private events;
|
|
35
|
+
private sessions;
|
|
36
|
+
constructor(provider: LLMProvider, events: EventBus, sessions: SessionStore);
|
|
37
|
+
run(agent: AgentConfig, input: string, session_id?: string): Promise<AgentResult>;
|
|
38
|
+
private executeTool;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare class InMemorySessionStore implements SessionStore {
|
|
42
|
+
private sessions;
|
|
43
|
+
create(agent_name: string): Session;
|
|
44
|
+
get(id: string): Session | undefined;
|
|
45
|
+
update(id: string, messages: ChatMessage[]): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
declare class ScoreLoader {
|
|
49
|
+
/**
|
|
50
|
+
* Dynamically import a tutti.score.ts file and return its config.
|
|
51
|
+
* Expects the file to `export default defineScore({ ... })`.
|
|
52
|
+
*/
|
|
53
|
+
static load(path: string): Promise<ScoreConfig>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Typed identity function for defining a Tutti score.
|
|
58
|
+
* Provides autocomplete and type checking — no runtime magic.
|
|
59
|
+
*/
|
|
60
|
+
declare function defineScore(config: ScoreConfig): ScoreConfig;
|
|
61
|
+
|
|
62
|
+
interface AnthropicProviderOptions {
|
|
63
|
+
api_key?: string;
|
|
64
|
+
}
|
|
65
|
+
declare class AnthropicProvider implements LLMProvider {
|
|
66
|
+
private client;
|
|
67
|
+
constructor(options?: AnthropicProviderOptions);
|
|
68
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { AgentRunner, AnthropicProvider, type AnthropicProviderOptions, EventBus, InMemorySessionStore, ScoreLoader, TuttiRuntime, defineScore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// src/agent-runner.ts
|
|
2
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
+
var DEFAULT_MAX_TURNS = 10;
|
|
4
|
+
var AgentRunner = class {
|
|
5
|
+
constructor(provider, events, sessions) {
|
|
6
|
+
this.provider = provider;
|
|
7
|
+
this.events = events;
|
|
8
|
+
this.sessions = sessions;
|
|
9
|
+
}
|
|
10
|
+
provider;
|
|
11
|
+
events;
|
|
12
|
+
sessions;
|
|
13
|
+
async run(agent, input, session_id) {
|
|
14
|
+
const session = session_id ? this.sessions.get(session_id) : this.sessions.create(agent.name);
|
|
15
|
+
if (!session) {
|
|
16
|
+
throw new Error(`Session not found: ${session_id}`);
|
|
17
|
+
}
|
|
18
|
+
this.events.emit({
|
|
19
|
+
type: "agent:start",
|
|
20
|
+
agent_name: agent.name,
|
|
21
|
+
session_id: session.id
|
|
22
|
+
});
|
|
23
|
+
const allTools = agent.voices.flatMap((v) => v.tools);
|
|
24
|
+
const toolDefs = allTools.map(toolToDefinition);
|
|
25
|
+
const messages = [
|
|
26
|
+
...session.messages,
|
|
27
|
+
{ role: "user", content: input }
|
|
28
|
+
];
|
|
29
|
+
const maxTurns = agent.max_turns ?? DEFAULT_MAX_TURNS;
|
|
30
|
+
const totalUsage = { input_tokens: 0, output_tokens: 0 };
|
|
31
|
+
let turns = 0;
|
|
32
|
+
while (turns < maxTurns) {
|
|
33
|
+
turns++;
|
|
34
|
+
this.events.emit({
|
|
35
|
+
type: "turn:start",
|
|
36
|
+
agent_name: agent.name,
|
|
37
|
+
session_id: session.id,
|
|
38
|
+
turn: turns
|
|
39
|
+
});
|
|
40
|
+
const request = {
|
|
41
|
+
model: agent.model,
|
|
42
|
+
system: agent.system_prompt,
|
|
43
|
+
messages,
|
|
44
|
+
tools: toolDefs.length > 0 ? toolDefs : void 0
|
|
45
|
+
};
|
|
46
|
+
this.events.emit({
|
|
47
|
+
type: "llm:request",
|
|
48
|
+
agent_name: agent.name,
|
|
49
|
+
request
|
|
50
|
+
});
|
|
51
|
+
const response = await this.provider.chat(request);
|
|
52
|
+
this.events.emit({
|
|
53
|
+
type: "llm:response",
|
|
54
|
+
agent_name: agent.name,
|
|
55
|
+
response
|
|
56
|
+
});
|
|
57
|
+
totalUsage.input_tokens += response.usage.input_tokens;
|
|
58
|
+
totalUsage.output_tokens += response.usage.output_tokens;
|
|
59
|
+
messages.push({ role: "assistant", content: response.content });
|
|
60
|
+
this.events.emit({
|
|
61
|
+
type: "turn:end",
|
|
62
|
+
agent_name: agent.name,
|
|
63
|
+
session_id: session.id,
|
|
64
|
+
turn: turns
|
|
65
|
+
});
|
|
66
|
+
if (response.stop_reason !== "tool_use") {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const toolUseBlocks = response.content.filter(
|
|
70
|
+
(b) => b.type === "tool_use"
|
|
71
|
+
);
|
|
72
|
+
const toolResults = await Promise.all(
|
|
73
|
+
toolUseBlocks.map(
|
|
74
|
+
(block) => this.executeTool(allTools, block, {
|
|
75
|
+
session_id: session.id,
|
|
76
|
+
agent_name: agent.name
|
|
77
|
+
})
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
messages.push({ role: "user", content: toolResults });
|
|
81
|
+
}
|
|
82
|
+
this.sessions.update(session.id, messages);
|
|
83
|
+
const lastAssistant = messages.filter((m) => m.role === "assistant").at(-1);
|
|
84
|
+
const output = extractText(lastAssistant?.content);
|
|
85
|
+
this.events.emit({
|
|
86
|
+
type: "agent:end",
|
|
87
|
+
agent_name: agent.name,
|
|
88
|
+
session_id: session.id
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
session_id: session.id,
|
|
92
|
+
output,
|
|
93
|
+
messages,
|
|
94
|
+
turns,
|
|
95
|
+
usage: totalUsage
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async executeTool(tools, block, context) {
|
|
99
|
+
const tool = tools.find((t) => t.name === block.name);
|
|
100
|
+
if (!tool) {
|
|
101
|
+
return {
|
|
102
|
+
type: "tool_result",
|
|
103
|
+
tool_use_id: block.id,
|
|
104
|
+
content: `Tool not found: ${block.name}`,
|
|
105
|
+
is_error: true
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
this.events.emit({
|
|
109
|
+
type: "tool:start",
|
|
110
|
+
agent_name: context.agent_name,
|
|
111
|
+
tool_name: block.name,
|
|
112
|
+
input: block.input
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
const parsed = tool.parameters.parse(block.input);
|
|
116
|
+
const result = await tool.execute(parsed, context);
|
|
117
|
+
this.events.emit({
|
|
118
|
+
type: "tool:end",
|
|
119
|
+
agent_name: context.agent_name,
|
|
120
|
+
tool_name: block.name,
|
|
121
|
+
result
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
type: "tool_result",
|
|
125
|
+
tool_use_id: block.id,
|
|
126
|
+
content: result.content,
|
|
127
|
+
is_error: result.is_error
|
|
128
|
+
};
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
+
this.events.emit({
|
|
132
|
+
type: "tool:error",
|
|
133
|
+
agent_name: context.agent_name,
|
|
134
|
+
tool_name: block.name,
|
|
135
|
+
error: error instanceof Error ? error : new Error(message)
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
type: "tool_result",
|
|
139
|
+
tool_use_id: block.id,
|
|
140
|
+
content: `Tool execution error: ${message}`,
|
|
141
|
+
is_error: true
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
function toolToDefinition(tool) {
|
|
147
|
+
const jsonSchema = zodToJsonSchema(tool.parameters, { target: "openApi3" });
|
|
148
|
+
return {
|
|
149
|
+
name: tool.name,
|
|
150
|
+
description: tool.description,
|
|
151
|
+
input_schema: jsonSchema
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function extractText(content) {
|
|
155
|
+
if (!content) return "";
|
|
156
|
+
if (typeof content === "string") return content;
|
|
157
|
+
return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/event-bus.ts
|
|
161
|
+
var EventBus = class {
|
|
162
|
+
listeners = /* @__PURE__ */ new Map();
|
|
163
|
+
on(type, handler) {
|
|
164
|
+
if (!this.listeners.has(type)) {
|
|
165
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
166
|
+
}
|
|
167
|
+
const handlers = this.listeners.get(type);
|
|
168
|
+
const h = handler;
|
|
169
|
+
handlers.add(h);
|
|
170
|
+
return () => {
|
|
171
|
+
handlers.delete(h);
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
off(type, handler) {
|
|
175
|
+
this.listeners.get(type)?.delete(handler);
|
|
176
|
+
}
|
|
177
|
+
emit(event) {
|
|
178
|
+
const handlers = this.listeners.get(event.type);
|
|
179
|
+
if (handlers) {
|
|
180
|
+
for (const handler of handlers) {
|
|
181
|
+
handler(event);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const wildcardHandlers = this.listeners.get("*");
|
|
185
|
+
if (wildcardHandlers) {
|
|
186
|
+
for (const handler of wildcardHandlers) {
|
|
187
|
+
handler(event);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** Subscribe to all events. */
|
|
192
|
+
onAny(handler) {
|
|
193
|
+
if (!this.listeners.has("*")) {
|
|
194
|
+
this.listeners.set("*", /* @__PURE__ */ new Set());
|
|
195
|
+
}
|
|
196
|
+
const handlers = this.listeners.get("*");
|
|
197
|
+
const h = handler;
|
|
198
|
+
handlers.add(h);
|
|
199
|
+
return () => {
|
|
200
|
+
handlers.delete(h);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/session-store.ts
|
|
206
|
+
import { randomUUID } from "crypto";
|
|
207
|
+
var InMemorySessionStore = class {
|
|
208
|
+
sessions = /* @__PURE__ */ new Map();
|
|
209
|
+
create(agent_name) {
|
|
210
|
+
const session = {
|
|
211
|
+
id: randomUUID(),
|
|
212
|
+
agent_name,
|
|
213
|
+
messages: [],
|
|
214
|
+
created_at: /* @__PURE__ */ new Date(),
|
|
215
|
+
updated_at: /* @__PURE__ */ new Date()
|
|
216
|
+
};
|
|
217
|
+
this.sessions.set(session.id, session);
|
|
218
|
+
return session;
|
|
219
|
+
}
|
|
220
|
+
get(id) {
|
|
221
|
+
return this.sessions.get(id);
|
|
222
|
+
}
|
|
223
|
+
update(id, messages) {
|
|
224
|
+
const session = this.sessions.get(id);
|
|
225
|
+
if (!session) {
|
|
226
|
+
throw new Error(`Session not found: ${id}`);
|
|
227
|
+
}
|
|
228
|
+
session.messages = messages;
|
|
229
|
+
session.updated_at = /* @__PURE__ */ new Date();
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/runtime.ts
|
|
234
|
+
var TuttiRuntime = class {
|
|
235
|
+
events;
|
|
236
|
+
sessions;
|
|
237
|
+
runner;
|
|
238
|
+
score;
|
|
239
|
+
constructor(score) {
|
|
240
|
+
this.score = score;
|
|
241
|
+
this.events = new EventBus();
|
|
242
|
+
this.sessions = new InMemorySessionStore();
|
|
243
|
+
this.runner = new AgentRunner(score.provider, this.events, this.sessions);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Run an agent by name with the given user input.
|
|
247
|
+
* Optionally pass a session_id to continue a conversation.
|
|
248
|
+
*/
|
|
249
|
+
async run(agent_name, input, session_id) {
|
|
250
|
+
const agent = this.score.agents[agent_name];
|
|
251
|
+
if (!agent) {
|
|
252
|
+
const available = Object.keys(this.score.agents).join(", ");
|
|
253
|
+
throw new Error(
|
|
254
|
+
`Agent "${agent_name}" not found. Available agents: ${available}`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const resolvedAgent = agent.model ? agent : { ...agent, model: this.score.default_model ?? "claude-sonnet-4-20250514" };
|
|
258
|
+
return this.runner.run(resolvedAgent, input, session_id);
|
|
259
|
+
}
|
|
260
|
+
/** Retrieve an existing session. */
|
|
261
|
+
getSession(id) {
|
|
262
|
+
return this.sessions.get(id);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/score-loader.ts
|
|
267
|
+
import { pathToFileURL } from "url";
|
|
268
|
+
import { resolve } from "path";
|
|
269
|
+
var ScoreLoader = class {
|
|
270
|
+
/**
|
|
271
|
+
* Dynamically import a tutti.score.ts file and return its config.
|
|
272
|
+
* Expects the file to `export default defineScore({ ... })`.
|
|
273
|
+
*/
|
|
274
|
+
static async load(path) {
|
|
275
|
+
const absolute = resolve(path);
|
|
276
|
+
const url = pathToFileURL(absolute).href;
|
|
277
|
+
const mod = await import(url);
|
|
278
|
+
if (!mod.default) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Score file must have a default export: ${path}`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return mod.default;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// src/define-score.ts
|
|
288
|
+
function defineScore(config) {
|
|
289
|
+
return config;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/providers/anthropic.ts
|
|
293
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
294
|
+
var AnthropicProvider = class {
|
|
295
|
+
client;
|
|
296
|
+
constructor(options = {}) {
|
|
297
|
+
this.client = new Anthropic({
|
|
298
|
+
apiKey: options.api_key
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async chat(request) {
|
|
302
|
+
if (!request.model) {
|
|
303
|
+
throw new Error("AnthropicProvider requires a model on ChatRequest");
|
|
304
|
+
}
|
|
305
|
+
const response = await this.client.messages.create({
|
|
306
|
+
model: request.model,
|
|
307
|
+
max_tokens: request.max_tokens ?? 4096,
|
|
308
|
+
system: request.system ?? "",
|
|
309
|
+
messages: request.messages.map((msg) => ({
|
|
310
|
+
role: msg.role,
|
|
311
|
+
content: msg.content
|
|
312
|
+
})),
|
|
313
|
+
tools: request.tools?.map((tool) => ({
|
|
314
|
+
name: tool.name,
|
|
315
|
+
description: tool.description,
|
|
316
|
+
input_schema: tool.input_schema
|
|
317
|
+
})),
|
|
318
|
+
...request.temperature != null && { temperature: request.temperature },
|
|
319
|
+
...request.stop_sequences && { stop_sequences: request.stop_sequences }
|
|
320
|
+
});
|
|
321
|
+
const content = response.content.map((block) => {
|
|
322
|
+
if (block.type === "text") {
|
|
323
|
+
return { type: "text", text: block.text };
|
|
324
|
+
}
|
|
325
|
+
if (block.type === "tool_use") {
|
|
326
|
+
return {
|
|
327
|
+
type: "tool_use",
|
|
328
|
+
id: block.id,
|
|
329
|
+
name: block.name,
|
|
330
|
+
input: block.input
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
throw new Error(`Unexpected content block type: ${block.type}`);
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
id: response.id,
|
|
337
|
+
content,
|
|
338
|
+
stop_reason: response.stop_reason,
|
|
339
|
+
usage: {
|
|
340
|
+
input_tokens: response.usage.input_tokens,
|
|
341
|
+
output_tokens: response.usage.output_tokens
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
export {
|
|
347
|
+
AgentRunner,
|
|
348
|
+
AnthropicProvider,
|
|
349
|
+
EventBus,
|
|
350
|
+
InMemorySessionStore,
|
|
351
|
+
ScoreLoader,
|
|
352
|
+
TuttiRuntime,
|
|
353
|
+
defineScore
|
|
354
|
+
};
|
|
355
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent-runner.ts","../src/event-bus.ts","../src/session-store.ts","../src/runtime.ts","../src/score-loader.ts","../src/define-score.ts","../src/providers/anthropic.ts"],"sourcesContent":["import { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type {\n AgentConfig,\n AgentResult,\n ChatMessage,\n ContentBlock,\n LLMProvider,\n SessionStore,\n Tool,\n ToolContext,\n ToolDefinition,\n ToolResultBlock,\n ToolUseBlock,\n TokenUsage,\n} from \"@tuttiai/types\";\nimport type { EventBus } from \"./event-bus.js\";\n\nconst DEFAULT_MAX_TURNS = 10;\n\nexport class AgentRunner {\n constructor(\n private provider: LLMProvider,\n private events: EventBus,\n private sessions: SessionStore,\n ) {}\n\n async run(\n agent: AgentConfig,\n input: string,\n session_id?: string,\n ): Promise<AgentResult> {\n // Resolve or create session\n const session = session_id\n ? this.sessions.get(session_id)\n : this.sessions.create(agent.name);\n\n if (!session) {\n throw new Error(`Session not found: ${session_id}`);\n }\n\n this.events.emit({\n type: \"agent:start\",\n agent_name: agent.name,\n session_id: session.id,\n });\n\n // Collect all tools from all voices\n const allTools = agent.voices.flatMap((v) => v.tools);\n const toolDefs = allTools.map(toolToDefinition);\n\n // Add user message\n const messages: ChatMessage[] = [\n ...session.messages,\n { role: \"user\", content: input },\n ];\n\n const maxTurns = agent.max_turns ?? DEFAULT_MAX_TURNS;\n const totalUsage: TokenUsage = { input_tokens: 0, output_tokens: 0 };\n let turns = 0;\n\n // Agentic loop\n while (turns < maxTurns) {\n turns++;\n\n this.events.emit({\n type: \"turn:start\",\n agent_name: agent.name,\n session_id: session.id,\n turn: turns,\n });\n\n const request = {\n model: agent.model,\n system: agent.system_prompt,\n messages,\n tools: toolDefs.length > 0 ? toolDefs : undefined,\n };\n\n this.events.emit({\n type: \"llm:request\",\n agent_name: agent.name,\n request,\n });\n\n const response = await this.provider.chat(request);\n\n this.events.emit({\n type: \"llm:response\",\n agent_name: agent.name,\n response,\n });\n\n totalUsage.input_tokens += response.usage.input_tokens;\n totalUsage.output_tokens += response.usage.output_tokens;\n\n // Add assistant message\n messages.push({ role: \"assistant\", content: response.content });\n\n this.events.emit({\n type: \"turn:end\",\n agent_name: agent.name,\n session_id: session.id,\n turn: turns,\n });\n\n // If the model is done talking, exit the loop\n if (response.stop_reason !== \"tool_use\") {\n break;\n }\n\n // Execute tool calls\n const toolUseBlocks = response.content.filter(\n (b): b is ToolUseBlock => b.type === \"tool_use\",\n );\n\n const toolResults: ToolResultBlock[] = await Promise.all(\n toolUseBlocks.map((block) =>\n this.executeTool(allTools, block, {\n session_id: session.id,\n agent_name: agent.name,\n }),\n ),\n );\n\n // Add tool results as a user message (Anthropic API format)\n messages.push({ role: \"user\", content: toolResults });\n }\n\n // Persist updated messages\n this.sessions.update(session.id, messages);\n\n // Extract final text output\n const lastAssistant = messages\n .filter((m) => m.role === \"assistant\")\n .at(-1);\n\n const output = extractText(lastAssistant?.content);\n\n this.events.emit({\n type: \"agent:end\",\n agent_name: agent.name,\n session_id: session.id,\n });\n\n return {\n session_id: session.id,\n output,\n messages,\n turns,\n usage: totalUsage,\n };\n }\n\n private async executeTool(\n tools: Tool[],\n block: ToolUseBlock,\n context: ToolContext,\n ): Promise<ToolResultBlock> {\n const tool = tools.find((t) => t.name === block.name);\n if (!tool) {\n return {\n type: \"tool_result\",\n tool_use_id: block.id,\n content: `Tool not found: ${block.name}`,\n is_error: true,\n };\n }\n\n this.events.emit({\n type: \"tool:start\",\n agent_name: context.agent_name,\n tool_name: block.name,\n input: block.input,\n });\n\n try {\n // Validate input with Zod\n const parsed = tool.parameters.parse(block.input);\n const result = await tool.execute(parsed, context);\n\n this.events.emit({\n type: \"tool:end\",\n agent_name: context.agent_name,\n tool_name: block.name,\n result,\n });\n\n return {\n type: \"tool_result\",\n tool_use_id: block.id,\n content: result.content,\n is_error: result.is_error,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.events.emit({\n type: \"tool:error\",\n agent_name: context.agent_name,\n tool_name: block.name,\n error: error instanceof Error ? error : new Error(message),\n });\n\n return {\n type: \"tool_result\",\n tool_use_id: block.id,\n content: `Tool execution error: ${message}`,\n is_error: true,\n };\n }\n }\n}\n\nfunction toolToDefinition(tool: Tool): ToolDefinition {\n const jsonSchema = zodToJsonSchema(tool.parameters, { target: \"openApi3\" });\n return {\n name: tool.name,\n description: tool.description,\n input_schema: jsonSchema as Record<string, unknown>,\n };\n}\n\nfunction extractText(content: string | ContentBlock[] | undefined): string {\n if (!content) return \"\";\n if (typeof content === \"string\") return content;\n return content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { text: string }).text)\n .join(\"\\n\");\n}\n","import type { TuttiEvent, TuttiEventType } from \"@tuttiai/types\";\n\ntype Handler = (event: never) => void;\n\nexport class EventBus {\n private listeners = new Map<string, Set<Handler>>();\n\n on<T extends TuttiEventType>(\n type: T,\n handler: (event: Extract<TuttiEvent, { type: T }>) => void,\n ): () => void {\n if (!this.listeners.has(type)) {\n this.listeners.set(type, new Set());\n }\n const handlers = this.listeners.get(type)!;\n const h = handler as Handler;\n handlers.add(h);\n\n return () => {\n handlers.delete(h);\n };\n }\n\n off<T extends TuttiEventType>(\n type: T,\n handler: (event: Extract<TuttiEvent, { type: T }>) => void,\n ): void {\n this.listeners.get(type)?.delete(handler as Handler);\n }\n\n emit(event: TuttiEvent): void {\n const handlers = this.listeners.get(event.type);\n if (handlers) {\n for (const handler of handlers) {\n (handler as (event: TuttiEvent) => void)(event);\n }\n }\n\n // Also notify wildcard listeners\n const wildcardHandlers = this.listeners.get(\"*\");\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n (handler as (event: TuttiEvent) => void)(event);\n }\n }\n }\n\n /** Subscribe to all events. */\n onAny(handler: (event: TuttiEvent) => void): () => void {\n if (!this.listeners.has(\"*\")) {\n this.listeners.set(\"*\", new Set());\n }\n const handlers = this.listeners.get(\"*\")!;\n const h = handler as Handler;\n handlers.add(h);\n\n return () => {\n handlers.delete(h);\n };\n }\n}\n","import type { Session, SessionStore, ChatMessage } from \"@tuttiai/types\";\nimport { randomUUID } from \"node:crypto\";\n\nexport class InMemorySessionStore implements SessionStore {\n private sessions = new Map<string, Session>();\n\n create(agent_name: string): Session {\n const session: Session = {\n id: randomUUID(),\n agent_name,\n messages: [],\n created_at: new Date(),\n updated_at: new Date(),\n };\n this.sessions.set(session.id, session);\n return session;\n }\n\n get(id: string): Session | undefined {\n return this.sessions.get(id);\n }\n\n update(id: string, messages: ChatMessage[]): void {\n const session = this.sessions.get(id);\n if (!session) {\n throw new Error(`Session not found: ${id}`);\n }\n session.messages = messages;\n session.updated_at = new Date();\n }\n}\n","import type { AgentResult, ScoreConfig, Session } from \"@tuttiai/types\";\nimport { AgentRunner } from \"./agent-runner.js\";\nimport { EventBus } from \"./event-bus.js\";\nimport { InMemorySessionStore } from \"./session-store.js\";\n\nexport class TuttiRuntime {\n readonly events: EventBus;\n private sessions: InMemorySessionStore;\n private runner: AgentRunner;\n private score: ScoreConfig;\n\n constructor(score: ScoreConfig) {\n this.score = score;\n this.events = new EventBus();\n this.sessions = new InMemorySessionStore();\n this.runner = new AgentRunner(score.provider, this.events, this.sessions);\n }\n\n /**\n * Run an agent by name with the given user input.\n * Optionally pass a session_id to continue a conversation.\n */\n async run(\n agent_name: string,\n input: string,\n session_id?: string,\n ): Promise<AgentResult> {\n const agent = this.score.agents[agent_name];\n if (!agent) {\n const available = Object.keys(this.score.agents).join(\", \");\n throw new Error(\n `Agent \"${agent_name}\" not found. Available agents: ${available}`,\n );\n }\n\n // Apply default model if agent doesn't specify one\n const resolvedAgent = agent.model\n ? agent\n : { ...agent, model: this.score.default_model ?? \"claude-sonnet-4-20250514\" };\n\n return this.runner.run(resolvedAgent, input, session_id);\n }\n\n /** Retrieve an existing session. */\n getSession(id: string): Session | undefined {\n return this.sessions.get(id);\n }\n}\n","import { pathToFileURL } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport type { ScoreConfig } from \"@tuttiai/types\";\n\nexport class ScoreLoader {\n /**\n * Dynamically import a tutti.score.ts file and return its config.\n * Expects the file to `export default defineScore({ ... })`.\n */\n static async load(path: string): Promise<ScoreConfig> {\n const absolute = resolve(path);\n const url = pathToFileURL(absolute).href;\n\n const mod = (await import(url)) as { default?: ScoreConfig };\n\n if (!mod.default) {\n throw new Error(\n `Score file must have a default export: ${path}`,\n );\n }\n\n return mod.default;\n }\n}\n","import type { ScoreConfig } from \"@tuttiai/types\";\n\n/**\n * Typed identity function for defining a Tutti score.\n * Provides autocomplete and type checking — no runtime magic.\n */\nexport function defineScore(config: ScoreConfig): ScoreConfig {\n return config;\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n LLMProvider,\n ChatRequest,\n ChatResponse,\n ContentBlock,\n} from \"@tuttiai/types\";\n\nexport interface AnthropicProviderOptions {\n api_key?: string;\n}\n\nexport class AnthropicProvider implements LLMProvider {\n private client: Anthropic;\n\n constructor(options: AnthropicProviderOptions = {}) {\n this.client = new Anthropic({\n apiKey: options.api_key,\n });\n }\n\n async chat(request: ChatRequest): Promise<ChatResponse> {\n if (!request.model) {\n throw new Error(\"AnthropicProvider requires a model on ChatRequest\");\n }\n\n const response = await this.client.messages.create({\n model: request.model,\n max_tokens: request.max_tokens ?? 4096,\n system: request.system ?? \"\",\n messages: request.messages.map((msg) => ({\n role: msg.role,\n content: msg.content as Anthropic.MessageCreateParams[\"messages\"][number][\"content\"],\n })),\n tools: request.tools?.map((tool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: tool.input_schema as Anthropic.Tool[\"input_schema\"],\n })),\n ...(request.temperature != null && { temperature: request.temperature }),\n ...(request.stop_sequences && { stop_sequences: request.stop_sequences }),\n });\n\n const content: ContentBlock[] = response.content.map((block) => {\n if (block.type === \"text\") {\n return { type: \"text\" as const, text: block.text };\n }\n if (block.type === \"tool_use\") {\n return {\n type: \"tool_use\" as const,\n id: block.id,\n name: block.name,\n input: block.input,\n };\n }\n throw new Error(`Unexpected content block type: ${(block as { type: string }).type}`);\n });\n\n return {\n id: response.id,\n content,\n stop_reason: response.stop_reason as ChatResponse[\"stop_reason\"],\n usage: {\n input_tokens: response.usage.input_tokens,\n output_tokens: response.usage.output_tokens,\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tuttiai/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tutti runtime — multi-agent orchestration for TypeScript",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
24
|
+
"@tuttiai/types": "^0.1.0",
|
|
25
|
+
"zod": "^3.24.0",
|
|
26
|
+
"zod-to-json-schema": "^3.24.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": ["tutti", "ai", "agents", "orchestration", "runtime", "llm", "anthropic"],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/tuttiai/tutti",
|
|
33
|
+
"directory": "packages/core"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://tutti-ai.com"
|
|
36
|
+
}
|