melony 0.1.55 → 0.2.1
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/README.md +85 -81
- package/dist/client.d.ts +12 -14
- package/dist/client.js +17 -52
- package/dist/client.js.map +1 -1
- package/dist/generate-id-DUrFIhxI.d.ts +118 -0
- package/dist/index.d.ts +62 -48
- package/dist/index.js +217 -262
- package/dist/index.js.map +1 -1
- package/package.json +4 -18
- package/dist/adapters/hono.d.ts +0 -9
- package/dist/adapters/hono.js +0 -29
- package/dist/adapters/hono.js.map +0 -1
- package/dist/chunk-N2OIHGFV.js +0 -138
- package/dist/chunk-N2OIHGFV.js.map +0 -1
- package/dist/chunk-PDXLHPTX.js +0 -30
- package/dist/chunk-PDXLHPTX.js.map +0 -1
- package/dist/generate-id-DU8kwYc2.d.ts +0 -3
- package/dist/plugins/require-approval.d.ts +0 -31
- package/dist/plugins/require-approval.js +0 -154
- package/dist/plugins/require-approval.js.map +0 -1
- package/dist/types-CE5iiNir.d.ts +0 -446
package/README.md
CHANGED
|
@@ -1,111 +1,115 @@
|
|
|
1
1
|
# melony
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
You write a `brain` (decides what to do) and `actions` (do work and stream events). Actions/brains can stream:
|
|
6
|
-
|
|
7
|
-
- **Text** (`{ type: "text" }` / `{ type: "text-delta" }`)
|
|
8
|
-
- **UI trees** (`event.ui = ui.card(...)`) that React (and other renderers) can display
|
|
9
|
-
- Any custom events you want (`{ type: "tool-result", data: ... }`)
|
|
3
|
+
Fast, unopinionated, minimalist event-based framework for AI agents.
|
|
10
4
|
|
|
11
5
|
## Installation
|
|
12
6
|
|
|
13
7
|
```bash
|
|
14
|
-
npm install melony
|
|
8
|
+
npm install melony
|
|
15
9
|
```
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### 1) Define an agent runtime
|
|
11
|
+
### 🔥 New: Fluent Builder API (Recommended)
|
|
20
12
|
|
|
21
13
|
```ts
|
|
22
|
-
import { melony
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
type: "text",
|
|
34
|
-
data: { content: `Searching for "${query}"...` },
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// SDUI: stream real UI to the frontend
|
|
38
|
-
yield {
|
|
39
|
-
type: "ui",
|
|
40
|
-
ui: ui.card({
|
|
41
|
-
title: "Results",
|
|
42
|
-
children: [
|
|
43
|
-
ui.list([
|
|
44
|
-
ui.listItem({ children: [ui.text("Product A — $10")] }),
|
|
45
|
-
ui.listItem({ children: [ui.text("Product B — $12")] }),
|
|
46
|
-
]),
|
|
47
|
-
],
|
|
48
|
-
}),
|
|
49
|
-
};
|
|
50
|
-
},
|
|
51
|
-
}),
|
|
52
|
-
},
|
|
14
|
+
import { melony } from "melony";
|
|
15
|
+
|
|
16
|
+
const agent = melony()
|
|
17
|
+
.action("getWeather", async function* ({ city }: { city: string }) {
|
|
18
|
+
yield { type: "text", data: { content: `Weather in ${city} is 24°C` } };
|
|
19
|
+
})
|
|
20
|
+
.on("text", async function* (event, { runtime }) {
|
|
21
|
+
if (event.data.content.includes("weather")) {
|
|
22
|
+
yield* runtime.execute("getWeather", { city: "London" });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
53
25
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
26
|
+
// Run it (or use agent.stream(event) for HTTP)
|
|
27
|
+
for await (const event of agent.build().run({ type: "text", data: { content: "How's the weather?" } })) {
|
|
28
|
+
console.log(event);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Legacy: Runtime Class API (Still Supported)
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { MelonyRuntime } from "melony";
|
|
36
|
+
|
|
37
|
+
// 1. Create the runtime
|
|
38
|
+
const agent = new MelonyRuntime({
|
|
39
|
+
actions: {
|
|
40
|
+
getWeather: {
|
|
41
|
+
name: "getWeather",
|
|
42
|
+
execute: async function* ({ city }: { city: string }) {
|
|
43
|
+
yield { type: "text", data: { content: `Weather in ${city} is sunny!` } };
|
|
44
|
+
}
|
|
61
45
|
}
|
|
62
46
|
},
|
|
47
|
+
eventHandlers: new Map([
|
|
48
|
+
["text", [async function* (event, { runtime }) {
|
|
49
|
+
yield* runtime.execute("getWeather", { city: "London" });
|
|
50
|
+
}]]
|
|
51
|
+
])
|
|
63
52
|
});
|
|
64
53
|
```
|
|
65
54
|
|
|
66
|
-
|
|
55
|
+
## Fluent Builder API
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
import { Hono } from "hono";
|
|
70
|
-
import { handle } from "melony/adapters/hono";
|
|
71
|
-
import { assistant } from "./assistant";
|
|
57
|
+
The fluent builder provides an excellent developer experience with method chaining:
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
### Action Definition
|
|
60
|
+
```ts
|
|
61
|
+
const agent = melony()
|
|
62
|
+
// Register an async generator with a name
|
|
63
|
+
.action("getWeather", async function* ({ city }) {
|
|
64
|
+
yield { type: "text", data: { content: `Weather in ${city} is sunny!` } };
|
|
65
|
+
});
|
|
75
66
|
```
|
|
76
67
|
|
|
77
|
-
###
|
|
78
|
-
|
|
68
|
+
### Event Handlers
|
|
79
69
|
```ts
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
console.log("event:", event);
|
|
92
|
-
}
|
|
70
|
+
const agent = melony()
|
|
71
|
+
.on("text", async function* (event, { runtime }) {
|
|
72
|
+
// Intercept and handle events
|
|
73
|
+
if (event.data.content.includes("help")) {
|
|
74
|
+
yield { type: "text", data: { content: "How can I help you?" } };
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.on("action:before", async function* (event) {
|
|
78
|
+
console.log(`Executing action: ${event.data.action}`);
|
|
79
|
+
})
|
|
80
|
+
.build();
|
|
93
81
|
```
|
|
94
82
|
|
|
95
|
-
|
|
83
|
+
### Plugin System
|
|
84
|
+
Plugins allow you to modularize and reuse actions and handlers across different agents. A plugin is simply a function that receives the `MelonyBuilder`.
|
|
96
85
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
```ts
|
|
87
|
+
import { melony, MelonyPlugin } from "melony";
|
|
88
|
+
|
|
89
|
+
const loggingPlugin: MelonyPlugin = (builder) => {
|
|
90
|
+
builder.on("action:before", async function* (event) {
|
|
91
|
+
console.log(`[Plugin] Executing: ${event.data.action}`);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const agent = melony()
|
|
96
|
+
.use(loggingPlugin)
|
|
97
|
+
.action("greet", async function* () {
|
|
98
|
+
yield { type: "text", data: { content: "Hello!" } };
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
### TypeScript Benefits
|
|
103
|
+
- **Full type inference** through the entire chain
|
|
104
|
+
- **IntelliSense** for all methods and parameters
|
|
105
|
+
- **Generic propagation** maintains type safety
|
|
106
|
+
- **Minimalist core** with zero required dependencies (except optional Zod)
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
## Core Concepts
|
|
105
109
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
110
|
+
- **Event**: The universal unit of streaming (`{ type, data, meta }`).
|
|
111
|
+
- **Action**: An async generator that yields events.
|
|
112
|
+
- **Event Handlers**: Reactive functions that listen to and emit events.
|
|
109
113
|
|
|
110
114
|
## License
|
|
111
115
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { E as Event
|
|
2
|
-
export {
|
|
3
|
-
import 'zod';
|
|
1
|
+
import { E as Event } from './generate-id-DUrFIhxI.js';
|
|
2
|
+
export { j as generateId } from './generate-id-DUrFIhxI.js';
|
|
4
3
|
|
|
5
|
-
interface ClientState {
|
|
6
|
-
events:
|
|
4
|
+
interface ClientState<TEvent extends Event = Event> {
|
|
5
|
+
events: TEvent[];
|
|
7
6
|
isLoading: boolean;
|
|
8
7
|
error: Error | null;
|
|
9
8
|
loadingStatus?: {
|
|
@@ -11,22 +10,22 @@ interface ClientState {
|
|
|
11
10
|
details?: string;
|
|
12
11
|
};
|
|
13
12
|
}
|
|
14
|
-
interface MelonyClientOptions {
|
|
13
|
+
interface MelonyClientOptions<TEvent extends Event = Event> {
|
|
15
14
|
url: string;
|
|
16
|
-
initialEvents?:
|
|
15
|
+
initialEvents?: TEvent[];
|
|
17
16
|
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
18
17
|
}
|
|
19
|
-
declare class MelonyClient {
|
|
18
|
+
declare class MelonyClient<TEvent extends Event = Event> {
|
|
20
19
|
private state;
|
|
21
20
|
readonly url: string;
|
|
22
21
|
private headers?;
|
|
23
22
|
private lastServerState;
|
|
24
23
|
private abortController;
|
|
25
24
|
private stateListeners;
|
|
26
|
-
constructor(options: MelonyClientOptions);
|
|
27
|
-
subscribe(listener: (state: ClientState) => void): () => void;
|
|
25
|
+
constructor(options: MelonyClientOptions<TEvent>);
|
|
26
|
+
subscribe(listener: (state: ClientState<TEvent>) => void): () => void;
|
|
28
27
|
getState(): {
|
|
29
|
-
events:
|
|
28
|
+
events: TEvent[];
|
|
30
29
|
isLoading: boolean;
|
|
31
30
|
error: Error | null;
|
|
32
31
|
loadingStatus?: {
|
|
@@ -35,11 +34,10 @@ declare class MelonyClient {
|
|
|
35
34
|
};
|
|
36
35
|
};
|
|
37
36
|
private getRequestHeaders;
|
|
38
|
-
getConfig(): Promise<Config>;
|
|
39
37
|
private setState;
|
|
40
|
-
sendEvent(event:
|
|
38
|
+
sendEvent(event: TEvent): AsyncGenerator<TEvent>;
|
|
41
39
|
private handleIncomingEvent;
|
|
42
|
-
reset(events?:
|
|
40
|
+
reset(events?: TEvent[]): void;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
export { type ClientState, Event, MelonyClient, type MelonyClientOptions };
|
package/dist/client.js
CHANGED
|
@@ -35,15 +35,6 @@ var MelonyClient = class {
|
|
|
35
35
|
}
|
|
36
36
|
return headers;
|
|
37
37
|
}
|
|
38
|
-
async getConfig() {
|
|
39
|
-
const headers = await this.getRequestHeaders();
|
|
40
|
-
const response = await fetch(this.url, {
|
|
41
|
-
method: "GET",
|
|
42
|
-
headers
|
|
43
|
-
});
|
|
44
|
-
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
45
|
-
return response.json();
|
|
46
|
-
}
|
|
47
38
|
setState(updates) {
|
|
48
39
|
this.state = { ...this.state, ...updates };
|
|
49
40
|
this.stateListeners.forEach((l) => l(this.getState()));
|
|
@@ -51,19 +42,22 @@ var MelonyClient = class {
|
|
|
51
42
|
async *sendEvent(event) {
|
|
52
43
|
if (this.abortController) this.abortController.abort();
|
|
53
44
|
this.abortController = new AbortController();
|
|
54
|
-
const runId = event.runId ?? generateId();
|
|
55
|
-
const state = event.state ?? this.lastServerState;
|
|
45
|
+
const runId = event.meta?.runId ?? generateId();
|
|
46
|
+
const state = event.meta?.state ?? this.lastServerState;
|
|
56
47
|
const optimisticEvent = {
|
|
57
48
|
...event,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
meta: {
|
|
50
|
+
...event.meta,
|
|
51
|
+
id: event.meta?.id ?? generateId(),
|
|
52
|
+
runId,
|
|
53
|
+
state,
|
|
54
|
+
role: event.meta?.role ?? "user",
|
|
55
|
+
timestamp: event.meta?.timestamp ?? Date.now()
|
|
56
|
+
}
|
|
62
57
|
};
|
|
63
58
|
this.setState({
|
|
64
59
|
isLoading: true,
|
|
65
60
|
error: null,
|
|
66
|
-
loadingStatus: void 0,
|
|
67
61
|
events: [...this.state.events, optimisticEvent]
|
|
68
62
|
});
|
|
69
63
|
try {
|
|
@@ -97,51 +91,23 @@ var MelonyClient = class {
|
|
|
97
91
|
}
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
|
-
this.setState({ isLoading: false
|
|
94
|
+
this.setState({ isLoading: false });
|
|
101
95
|
} catch (err) {
|
|
102
96
|
if (err instanceof Error && err.name === "AbortError") {
|
|
103
|
-
this.setState({ isLoading: false
|
|
97
|
+
this.setState({ isLoading: false });
|
|
104
98
|
return;
|
|
105
99
|
}
|
|
106
100
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
107
|
-
this.setState({ error, isLoading: false
|
|
101
|
+
this.setState({ error, isLoading: false });
|
|
108
102
|
throw error;
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
105
|
handleIncomingEvent(event) {
|
|
112
|
-
if (event.state) {
|
|
113
|
-
this.lastServerState = event.state;
|
|
106
|
+
if (event.meta?.state) {
|
|
107
|
+
this.lastServerState = event.meta.state;
|
|
114
108
|
}
|
|
115
109
|
const events = [...this.state.events];
|
|
116
|
-
|
|
117
|
-
const currentStatus = this.state.loadingStatus;
|
|
118
|
-
const newMessage = event.data?.message ?? currentStatus?.message ?? "Processing...";
|
|
119
|
-
const newDelta = event.data?.delta;
|
|
120
|
-
let newDetails = currentStatus?.details ?? "";
|
|
121
|
-
if (newDelta !== void 0) {
|
|
122
|
-
newDetails += newDelta;
|
|
123
|
-
} else if (event.data?.details !== void 0) {
|
|
124
|
-
newDetails = event.data.details;
|
|
125
|
-
}
|
|
126
|
-
this.setState({
|
|
127
|
-
loadingStatus: {
|
|
128
|
-
message: newMessage,
|
|
129
|
-
details: newDetails || void 0
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
const lastEvent = events[events.length - 1];
|
|
134
|
-
if (event.type === "text-delta" && lastEvent?.type === "text-delta" && event.runId === lastEvent.runId && event.data?.delta) {
|
|
135
|
-
events[events.length - 1] = {
|
|
136
|
-
...lastEvent,
|
|
137
|
-
data: {
|
|
138
|
-
...lastEvent.data,
|
|
139
|
-
delta: (lastEvent.data?.delta || "") + event.data.delta
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
} else {
|
|
143
|
-
events.push(event);
|
|
144
|
-
}
|
|
110
|
+
events.push(event);
|
|
145
111
|
this.setState({ events });
|
|
146
112
|
}
|
|
147
113
|
reset(events = []) {
|
|
@@ -149,8 +115,7 @@ var MelonyClient = class {
|
|
|
149
115
|
this.setState({
|
|
150
116
|
events,
|
|
151
117
|
error: null,
|
|
152
|
-
isLoading: false
|
|
153
|
-
loadingStatus: void 0
|
|
118
|
+
isLoading: false
|
|
154
119
|
});
|
|
155
120
|
}
|
|
156
121
|
};
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["import { Config, Event } from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\nexport type { Event };\nexport { generateId };\n\nexport interface ClientState {\n events: Event[];\n isLoading: boolean;\n error: Error | null;\n loadingStatus?: {\n message: string;\n details?: string;\n };\n}\n\nexport interface MelonyClientOptions {\n url: string;\n initialEvents?: Event[];\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n}\n\nexport class MelonyClient {\n private state: ClientState;\n public readonly url: string;\n private headers?: MelonyClientOptions[\"headers\"];\n private lastServerState: any = null;\n private abortController: AbortController | null = null;\n private stateListeners: Set<(state: ClientState) => void> = new Set();\n\n constructor(options: MelonyClientOptions) {\n this.url = options.url;\n this.headers = options.headers;\n this.state = {\n events: options.initialEvents ?? [],\n isLoading: false,\n error: null,\n loadingStatus: undefined,\n };\n }\n\n subscribe(listener: (state: ClientState) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n getState() {\n return { ...this.state };\n }\n\n private async getRequestHeaders() {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.headers) {\n const extraHeaders =\n typeof this.headers === \"function\"\n ? await this.headers()\n : this.headers;\n Object.assign(headers, extraHeaders);\n }\n return headers;\n }\n\n async getConfig(): Promise<Config> {\n const headers = await this.getRequestHeaders();\n const response = await fetch(this.url, {\n method: \"GET\",\n headers,\n });\n\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\n return response.json();\n }\n\n private setState(updates: Partial<ClientState>) {\n this.state = { ...this.state, ...updates };\n this.stateListeners.forEach((l) => l(this.getState()));\n }\n\n async *sendEvent(event: Event): AsyncGenerator<Event> {\n if (this.abortController) this.abortController.abort();\n this.abortController = new AbortController();\n\n const runId = event.runId ?? generateId();\n const state = event.state ?? this.lastServerState;\n const optimisticEvent: Event = {\n ...event,\n runId,\n state,\n role: event.role ?? \"user\",\n timestamp: event.timestamp ?? Date.now(),\n };\n\n this.setState({\n isLoading: true,\n error: null,\n loadingStatus: undefined,\n events: [...this.state.events, optimisticEvent],\n });\n\n try {\n const headers = await this.getRequestHeaders();\n const response = await fetch(this.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ event: optimisticEvent }),\n signal: this.abortController.signal,\n });\n\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n if (!response.body) throw new Error(\"No response body\");\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n try {\n const incomingEvent: Event = JSON.parse(line.slice(6));\n this.handleIncomingEvent(incomingEvent);\n yield incomingEvent;\n } catch (e) {\n console.error(\"Failed to parse event\", e);\n }\n }\n }\n this.setState({ isLoading: false, loadingStatus: undefined });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n this.setState({ isLoading: false, loadingStatus: undefined });\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n this.setState({ error, isLoading: false, loadingStatus: undefined });\n throw error;\n }\n }\n\n private handleIncomingEvent(event: Event) {\n if (event.state) {\n this.lastServerState = event.state;\n }\n const events = [...this.state.events];\n\n if (event.type === \"loading-status\") {\n const currentStatus = this.state.loadingStatus;\n const newMessage =\n event.data?.message ?? currentStatus?.message ?? \"Processing...\";\n const newDelta = event.data?.delta;\n\n let newDetails = currentStatus?.details ?? \"\";\n if (newDelta !== undefined) {\n newDetails += newDelta;\n } else if (event.data?.details !== undefined) {\n newDetails = event.data.details;\n }\n\n this.setState({\n loadingStatus: {\n message: newMessage,\n details: newDetails || undefined,\n },\n });\n }\n\n const lastEvent = events[events.length - 1];\n if (\n event.type === \"text-delta\" &&\n lastEvent?.type === \"text-delta\" &&\n event.runId === lastEvent.runId &&\n event.data?.delta\n ) {\n events[events.length - 1] = {\n ...lastEvent,\n data: {\n ...lastEvent.data,\n delta: (lastEvent.data?.delta || \"\") + event.data.delta,\n },\n };\n } else {\n events.push(event);\n }\n\n this.setState({ events });\n }\n\n reset(events: Event[] = []) {\n if (this.abortController) this.abortController.abort();\n this.setState({\n events,\n error: null,\n isLoading: false,\n loadingStatus: undefined,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["import { Event } from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\nexport type { Event };\nexport { generateId };\n\nexport interface ClientState<TEvent extends Event = Event> {\n events: TEvent[];\n isLoading: boolean;\n error: Error | null;\n loadingStatus?: {\n message: string;\n details?: string;\n };\n}\n\nexport interface MelonyClientOptions<TEvent extends Event = Event> {\n url: string;\n initialEvents?: TEvent[];\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n}\n\nexport class MelonyClient<TEvent extends Event = Event> {\n private state: ClientState<TEvent>;\n public readonly url: string;\n private headers?: MelonyClientOptions<TEvent>[\"headers\"];\n private lastServerState: any = null;\n private abortController: AbortController | null = null;\n private stateListeners: Set<(state: ClientState<TEvent>) => void> = new Set();\n\n constructor(options: MelonyClientOptions<TEvent>) {\n this.url = options.url;\n this.headers = options.headers;\n this.state = {\n events: options.initialEvents ?? [],\n isLoading: false,\n error: null,\n loadingStatus: undefined,\n };\n }\n\n subscribe(listener: (state: ClientState<TEvent>) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n getState() {\n return { ...this.state };\n }\n\n private async getRequestHeaders() {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.headers) {\n const extraHeaders =\n typeof this.headers === \"function\"\n ? await this.headers()\n : this.headers;\n Object.assign(headers, extraHeaders);\n }\n return headers;\n }\n\n private setState(updates: Partial<ClientState<TEvent>>) {\n this.state = { ...this.state, ...updates };\n this.stateListeners.forEach((l) => l(this.getState()));\n }\n\n async *sendEvent(event: TEvent): AsyncGenerator<TEvent> {\n if (this.abortController) this.abortController.abort();\n this.abortController = new AbortController();\n\n const runId = event.meta?.runId ?? generateId();\n const state = event.meta?.state ?? this.lastServerState;\n const optimisticEvent: TEvent = {\n ...event,\n meta: {\n ...event.meta,\n id: event.meta?.id ?? generateId(),\n runId,\n state,\n role: event.meta?.role ?? \"user\",\n timestamp: event.meta?.timestamp ?? Date.now(),\n },\n } as TEvent;\n\n this.setState({\n isLoading: true,\n error: null,\n events: [...this.state.events, optimisticEvent],\n });\n\n try {\n const headers = await this.getRequestHeaders();\n const response = await fetch(this.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ event: optimisticEvent }),\n signal: this.abortController.signal,\n });\n\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n if (!response.body) throw new Error(\"No response body\");\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n try {\n const incomingEvent: TEvent = JSON.parse(line.slice(6));\n this.handleIncomingEvent(incomingEvent);\n yield incomingEvent;\n } catch (e) {\n console.error(\"Failed to parse event\", e);\n }\n }\n }\n this.setState({ isLoading: false });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n this.setState({ isLoading: false });\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n this.setState({ error, isLoading: false });\n throw error;\n }\n }\n\n private handleIncomingEvent(event: TEvent) {\n if (event.meta?.state) {\n this.lastServerState = event.meta.state;\n }\n\n const events = [...this.state.events];\n\n events.push(event);\n\n this.setState({ events });\n }\n\n reset(events: TEvent[] = []) {\n if (this.abortController) this.abortController.abort();\n this.setState({\n events,\n error: null,\n isLoading: false,\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Melony Runtime.
|
|
3
|
+
* Fully unopinionated - executes a single action per run.
|
|
4
|
+
* Chaining/looping is left to userland.
|
|
5
|
+
*/
|
|
6
|
+
declare class Runtime<TState = any, TEvent extends Event = Event> {
|
|
7
|
+
readonly config: Config<TState, TEvent>;
|
|
8
|
+
private queue;
|
|
9
|
+
private isEmitting;
|
|
10
|
+
constructor(config: Config<TState, TEvent>);
|
|
11
|
+
/**
|
|
12
|
+
* Run an action by name with given params.
|
|
13
|
+
* This is the primary way to execute actions.
|
|
14
|
+
*/
|
|
15
|
+
execute(actionName: string, params: any, options?: {
|
|
16
|
+
state?: TState;
|
|
17
|
+
runId?: string;
|
|
18
|
+
}): AsyncGenerator<TEvent>;
|
|
19
|
+
/**
|
|
20
|
+
* Process an incoming event through the runtime.
|
|
21
|
+
* All event processing is handled by user-defined event handlers.
|
|
22
|
+
*/
|
|
23
|
+
run(event: TEvent): AsyncGenerator<TEvent>;
|
|
24
|
+
/**
|
|
25
|
+
* Run all event handlers that match the given event type.
|
|
26
|
+
*/
|
|
27
|
+
private runEventHandlers;
|
|
28
|
+
/**
|
|
29
|
+
* Internal helper to yield an event with metadata.
|
|
30
|
+
* Handlers are invoked by runEventHandlers when events bubble up.
|
|
31
|
+
*/
|
|
32
|
+
private emit;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type Role = "user" | "assistant" | "system";
|
|
36
|
+
/**
|
|
37
|
+
* System-managed metadata for an event.
|
|
38
|
+
*/
|
|
39
|
+
interface EventMeta<TState = any> {
|
|
40
|
+
id: string;
|
|
41
|
+
runId: string;
|
|
42
|
+
timestamp: number;
|
|
43
|
+
role: Role;
|
|
44
|
+
state: TState;
|
|
45
|
+
[key: string]: any;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The core Event structure.
|
|
49
|
+
* Fully unopinionated - just type, data, and optional metadata.
|
|
50
|
+
*/
|
|
51
|
+
type Event<TData = any> = {
|
|
52
|
+
type: string;
|
|
53
|
+
data: TData;
|
|
54
|
+
meta?: EventMeta;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Built-in action lifecycle events emitted by the runtime.
|
|
58
|
+
*/
|
|
59
|
+
interface ActionBeforeEvent extends Event<{
|
|
60
|
+
action: string;
|
|
61
|
+
params: any;
|
|
62
|
+
}> {
|
|
63
|
+
type: "action:before";
|
|
64
|
+
}
|
|
65
|
+
interface ActionAfterEvent extends Event<{
|
|
66
|
+
action: string;
|
|
67
|
+
result: any;
|
|
68
|
+
}> {
|
|
69
|
+
type: "action:after";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Built-in call-action event for triggering action execution.
|
|
73
|
+
*/
|
|
74
|
+
interface CallActionEvent extends Event<{
|
|
75
|
+
action: string;
|
|
76
|
+
params: unknown;
|
|
77
|
+
}> {
|
|
78
|
+
type: "call-action";
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Built-in error event emitted by the runtime.
|
|
82
|
+
*/
|
|
83
|
+
interface ErrorEvent extends Event<{
|
|
84
|
+
message: string;
|
|
85
|
+
action?: string;
|
|
86
|
+
stack?: string;
|
|
87
|
+
}> {
|
|
88
|
+
type: "error";
|
|
89
|
+
}
|
|
90
|
+
type ActionExecute<TParams = any, TState = any, TEvent extends Event = Event> = (params: TParams, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, any, unknown>;
|
|
91
|
+
interface Action<TParams = any, TState = any, TEvent extends Event = Event> {
|
|
92
|
+
name: string;
|
|
93
|
+
execute: ActionExecute<TParams, TState, TEvent>;
|
|
94
|
+
}
|
|
95
|
+
interface RuntimeContext<TState = any, TEvent extends Event = Event> {
|
|
96
|
+
state: TState;
|
|
97
|
+
runId: string;
|
|
98
|
+
actions: Record<string, Action<any, TState, TEvent>>;
|
|
99
|
+
runtime: Runtime<TState, TEvent>;
|
|
100
|
+
/**
|
|
101
|
+
* Immediately interrupts the runtime execution.
|
|
102
|
+
* If an event is provided, it will be emitted before the runtime stops.
|
|
103
|
+
*/
|
|
104
|
+
suspend: (event?: TEvent) => never;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Event handler function for processing events.
|
|
108
|
+
* Can return events to emit or undefined to continue processing.
|
|
109
|
+
*/
|
|
110
|
+
type EventHandler<TState = any, TEvent extends Event = Event> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void;
|
|
111
|
+
interface Config<TState = any, TEvent extends Event = Event> {
|
|
112
|
+
actions: Record<string, Action<any, TState, TEvent>>;
|
|
113
|
+
eventHandlers: Map<string, EventHandler<TState, TEvent>[]>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
declare const generateId: () => string;
|
|
117
|
+
|
|
118
|
+
export { type ActionExecute as A, type Config as C, type Event as E, Runtime as R, type Action as a, type RuntimeContext as b, type Role as c, type EventMeta as d, type ActionBeforeEvent as e, type ActionAfterEvent as f, type CallActionEvent as g, type ErrorEvent as h, type EventHandler as i, generateId as j };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,53 +1,76 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { R as Runtime, A as ActionExecute, a as Action, E as Event, C as Config, b as RuntimeContext } from './generate-id-DUrFIhxI.js';
|
|
2
|
+
export { f as ActionAfterEvent, e as ActionBeforeEvent, g as CallActionEvent, h as ErrorEvent, i as EventHandler, d as EventMeta, c as Role, j as generateId } from './generate-id-DUrFIhxI.js';
|
|
3
|
+
|
|
4
|
+
declare const MelonyRuntime: typeof Runtime;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Create an action with just a name and handler.
|
|
8
|
+
* Compatible with any agent - types are resolved automatically when added to an agent.
|
|
9
9
|
*/
|
|
10
|
-
declare
|
|
11
|
-
private config;
|
|
12
|
-
constructor(config: Config<TState>);
|
|
13
|
-
run(event: Event): AsyncGenerator<Event>;
|
|
14
|
-
private dispatchToBrain;
|
|
15
|
-
private executeAction;
|
|
16
|
-
/**
|
|
17
|
-
* Internal helper to call a hook (generator) and yield its events.
|
|
18
|
-
*/
|
|
19
|
-
private callHook;
|
|
20
|
-
/**
|
|
21
|
-
* Internal helper to yield an event and trigger the onEvent hook.
|
|
22
|
-
*/
|
|
23
|
-
private emit;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
declare const melony: <TState = any>(config: Config<TState>) => {
|
|
27
|
-
config: Config<TState>;
|
|
28
|
-
run: (event: Event) => AsyncGenerator<Event>;
|
|
29
|
-
};
|
|
30
|
-
|
|
10
|
+
declare function action<TParams = any>(name: string, execute: ActionExecute<TParams, any, any>): Action<TParams, any, any>;
|
|
31
11
|
/**
|
|
32
|
-
* Helper to define an action with full type inference.
|
|
12
|
+
* Helper to define an action with full type inference (for advanced use cases).
|
|
33
13
|
*/
|
|
34
|
-
declare
|
|
14
|
+
declare function action<TParams = any, TState = any, TEvent extends Event = Event>(config: Action<TParams, TState, TEvent>): Action<TParams, TState, TEvent>;
|
|
35
15
|
|
|
36
16
|
/**
|
|
37
|
-
* A
|
|
38
|
-
*
|
|
17
|
+
* A Melony plugin is a function that receives the builder and extends it.
|
|
18
|
+
* This allows for modularizing common actions and handlers.
|
|
39
19
|
*/
|
|
40
|
-
|
|
20
|
+
type MelonyPlugin<TState = any, TEvent extends Event = Event> = (builder: MelonyBuilder<TState, TEvent>) => void;
|
|
41
21
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
22
|
+
* Fluent builder for creating Melony agents with excellent developer experience.
|
|
23
|
+
* Provides method chaining for actions and plugins with full TypeScript support.
|
|
44
24
|
*/
|
|
45
|
-
declare
|
|
46
|
-
|
|
25
|
+
declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
|
|
26
|
+
private config;
|
|
27
|
+
constructor(initialConfig?: Partial<Config<TState, TEvent>>);
|
|
28
|
+
/**
|
|
29
|
+
* Add built-in event handlers that are available in all agents.
|
|
30
|
+
*/
|
|
31
|
+
private addBuiltInHandlers;
|
|
32
|
+
/**
|
|
33
|
+
* Add an action to the agent with fluent method chaining.
|
|
34
|
+
* Supports two patterns:
|
|
35
|
+
* - Pass a pre-defined Action object
|
|
36
|
+
* - Define action inline with name and handler
|
|
37
|
+
*/
|
|
38
|
+
action<TParams = any>(action: Action<TParams, any, any>): this;
|
|
39
|
+
action<TParams = any>(name: string, execute: ActionExecute<TParams, any, any>): this;
|
|
40
|
+
/**
|
|
41
|
+
* Add an event handler for a specific event type. Supports method chaining.
|
|
42
|
+
* The handler receives the narrowed event type based on the eventType string.
|
|
43
|
+
*/
|
|
44
|
+
on<K extends TEvent["type"]>(eventType: K, handler: (event: Extract<TEvent, {
|
|
45
|
+
type: K;
|
|
46
|
+
}>, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void): this;
|
|
47
|
+
/**
|
|
48
|
+
* Use a plugin to extend the builder.
|
|
49
|
+
* This is ideal for modularizing common actions and handlers.
|
|
50
|
+
*/
|
|
51
|
+
use(plugin: MelonyPlugin<TState, TEvent>): this;
|
|
52
|
+
/**
|
|
53
|
+
* Build and return the Melony runtime instance.
|
|
54
|
+
* This is the final method in the fluent chain.
|
|
55
|
+
*/
|
|
56
|
+
build(): Runtime<TState, TEvent>;
|
|
57
|
+
/**
|
|
58
|
+
* Execute and stream the response for an event.
|
|
59
|
+
* This is a convenience method that builds the runtime and calls run().
|
|
60
|
+
*/
|
|
61
|
+
stream(event: TEvent, options?: {
|
|
62
|
+
state?: TState;
|
|
63
|
+
}): Promise<Response>;
|
|
64
|
+
/**
|
|
65
|
+
* Get the current configuration (useful for debugging or serialization).
|
|
66
|
+
*/
|
|
67
|
+
getConfig(): Config<TState, TEvent>;
|
|
68
|
+
}
|
|
47
69
|
/**
|
|
48
|
-
*
|
|
70
|
+
* Factory function to create a new Melony builder instance.
|
|
71
|
+
* This is the entry point for the fluent API.
|
|
49
72
|
*/
|
|
50
|
-
declare
|
|
73
|
+
declare function melony<TState = any, TEvent extends Event = Event>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent>;
|
|
51
74
|
|
|
52
75
|
/**
|
|
53
76
|
* Convert an async generator of events to an HTTP streaming response
|
|
@@ -55,13 +78,4 @@ declare const plugin: <TState = any>(config: Plugin<TState>) => Plugin<TState>;
|
|
|
55
78
|
*/
|
|
56
79
|
declare function createStreamResponse(generator: AsyncGenerator<Event>): Response;
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Filters a list of events by their slot property.
|
|
62
|
-
* If multiple events have the same slot, only the latest one is kept,
|
|
63
|
-
* but its position in the returned array corresponds to the first time that slot appeared.
|
|
64
|
-
*/
|
|
65
|
-
declare function filterEventsBySlots(events: Event[]): Event[];
|
|
66
|
-
|
|
67
|
-
export { Action, Brain, Config, Event, Message, Plugin, Runtime, action, convertEventsToMessages, cortex, createStreamResponse, filterEventsBySlots, lobe, melony, plugin };
|
|
81
|
+
export { Action, ActionExecute, Config, Event, MelonyBuilder, type MelonyPlugin, MelonyRuntime, Runtime, RuntimeContext, action, createStreamResponse, melony };
|