@yak-io/javascript 0.8.0 → 0.10.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/README.md +131 -107
- package/dist/client.d.ts +9 -47
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +15 -71
- package/dist/embed.d.ts +15 -0
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.js +9 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/toolset.d.ts +45 -0
- package/dist/toolset.d.ts.map +1 -0
- package/dist/toolset.js +119 -0
- package/dist/types/config.d.ts +3 -5
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +1 -17
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/tools.d.ts +35 -42
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/voice-machine.d.ts +16 -0
- package/dist/voice-machine.d.ts.map +1 -1
- package/dist/voice-machine.js +5 -0
- package/dist/voice-session.d.ts +12 -14
- package/dist/voice-session.d.ts.map +1 -1
- package/dist/voice-session.js +33 -45
- package/package.json +3 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ToolAdapter, YakToolset } from "./types/tools.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compose client-side {@link ToolAdapter}s into ONE merged tool manifest and ONE
|
|
4
|
+
* routed `onToolCall`. This is the single injection point for "available tools"
|
|
5
|
+
* into the iframe: every adapter — client-side (GraphQL/REST/custom, run by your own
|
|
6
|
+
* `execute` callback) or server-relayed ({@link createYakServerAdapter}) — contributes
|
|
7
|
+
* its tools to the merged manifest and its executor to the routed handler. Because every
|
|
8
|
+
* tool now flows through `onToolCall`, they all surface through `onToolCallComplete` /
|
|
9
|
+
* `useYakToolEvent`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const toolset = createYakToolset([
|
|
14
|
+
* createYakServerAdapter({ endpoint: "/api/yak" }), // tRPC + custom server tools
|
|
15
|
+
* createGraphQLToolAdapter({ name: "shop", schema, execute: runShopQuery }),
|
|
16
|
+
* createRESTToolAdapter({ name: "billing", spec, execute: callBillingApi }),
|
|
17
|
+
* ]);
|
|
18
|
+
*
|
|
19
|
+
* <YakProvider
|
|
20
|
+
* getConfig={async () => ({ routes, ...(await toolset.getConfig()) })}
|
|
21
|
+
* onToolCall={toolset.onToolCall}
|
|
22
|
+
* />;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function createYakToolset(adapters: ToolAdapter[]): YakToolset;
|
|
26
|
+
/** Config for {@link createYakServerAdapter}. */
|
|
27
|
+
export type YakServerAdapterConfig = {
|
|
28
|
+
/**
|
|
29
|
+
* Endpoint mounting `createYakHandler` / `createNextYakHandler`. GET returns the
|
|
30
|
+
* tool manifest; POST `{ name, args }` executes a tool. Default: `"/api/yak"`.
|
|
31
|
+
*/
|
|
32
|
+
endpoint?: string;
|
|
33
|
+
/** Stable id for diagnostics. Default: `"server"`. */
|
|
34
|
+
id?: string;
|
|
35
|
+
/** Extra headers (e.g. auth) applied to both the GET manifest load and POST execution. */
|
|
36
|
+
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Bridge a server-side yak handler ({@link createYakHandler} /
|
|
40
|
+
* `createNextYakHandler`, e.g. fronting the `@yak-io/trpc` adapter) into a
|
|
41
|
+
* client-side {@link ToolAdapter}, so server-relayed tools merge into the same
|
|
42
|
+
* manifest + `onToolCall` as browser-executed adapters.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createYakServerAdapter(config?: YakServerAdapterConfig): ToolAdapter;
|
|
45
|
+
//# sourceMappingURL=toolset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolset.d.ts","sourceRoot":"","sources":["../src/toolset.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAIX,UAAU,EACX,MAAM,kBAAkB,CAAC;AAO1B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,UAAU,CA4DpE;AAED,iDAAiD;AACjD,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0FAA0F;IAC1F,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;CACpE,CAAC;AAgBF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,sBAA2B,GAAG,WAAW,CA8BvF"}
|
package/dist/toolset.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { logger } from "./logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compose client-side {@link ToolAdapter}s into ONE merged tool manifest and ONE
|
|
4
|
+
* routed `onToolCall`. This is the single injection point for "available tools"
|
|
5
|
+
* into the iframe: every adapter — client-side (GraphQL/REST/custom, run by your own
|
|
6
|
+
* `execute` callback) or server-relayed ({@link createYakServerAdapter}) — contributes
|
|
7
|
+
* its tools to the merged manifest and its executor to the routed handler. Because every
|
|
8
|
+
* tool now flows through `onToolCall`, they all surface through `onToolCallComplete` /
|
|
9
|
+
* `useYakToolEvent`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const toolset = createYakToolset([
|
|
14
|
+
* createYakServerAdapter({ endpoint: "/api/yak" }), // tRPC + custom server tools
|
|
15
|
+
* createGraphQLToolAdapter({ name: "shop", schema, execute: runShopQuery }),
|
|
16
|
+
* createRESTToolAdapter({ name: "billing", spec, execute: callBillingApi }),
|
|
17
|
+
* ]);
|
|
18
|
+
*
|
|
19
|
+
* <YakProvider
|
|
20
|
+
* getConfig={async () => ({ routes, ...(await toolset.getConfig()) })}
|
|
21
|
+
* onToolCall={toolset.onToolCall}
|
|
22
|
+
* />;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function createYakToolset(adapters) {
|
|
26
|
+
let cache = null;
|
|
27
|
+
async function resolve(force = false) {
|
|
28
|
+
if (cache && !force)
|
|
29
|
+
return cache;
|
|
30
|
+
cache = await Promise.all(adapters.map(async (adapter) => ({ adapter, tools: await adapter.getTools() })));
|
|
31
|
+
return cache;
|
|
32
|
+
}
|
|
33
|
+
async function getConfig() {
|
|
34
|
+
// Refresh on every config load — tools may depend on page/user.
|
|
35
|
+
const resolved = await resolve(true);
|
|
36
|
+
const tools = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
for (const { adapter, tools: adapterTools } of resolved) {
|
|
39
|
+
for (const tool of adapterTools) {
|
|
40
|
+
if (seen.has(tool.name)) {
|
|
41
|
+
logger.warn(`Duplicate tool name "${tool.name}"; keeping the first and ignoring the one from adapter "${adapter.id ?? "unknown"}".`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
seen.add(tool.name);
|
|
45
|
+
tools.push(tool);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
tools: {
|
|
50
|
+
tools,
|
|
51
|
+
sources: resolved.map(({ adapter, tools: adapterTools }, index) => ({
|
|
52
|
+
id: adapter.id ?? `adapter-${index}`,
|
|
53
|
+
count: adapterTools.length,
|
|
54
|
+
})),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const onToolCall = async (name, args) => {
|
|
59
|
+
// Fast path: explicit ownership predicates need no resolution.
|
|
60
|
+
for (const adapter of adapters) {
|
|
61
|
+
if (adapter.ownsTool?.(name)) {
|
|
62
|
+
return adapter.execute(name, args);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Otherwise route by the adapter's resolved tool names.
|
|
66
|
+
const resolved = await resolve();
|
|
67
|
+
for (const { adapter, tools } of resolved) {
|
|
68
|
+
if (adapter.ownsTool)
|
|
69
|
+
continue; // already consulted above
|
|
70
|
+
if (tools.some((tool) => tool.name === name)) {
|
|
71
|
+
return adapter.execute(name, args);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
75
|
+
};
|
|
76
|
+
return { getConfig, onToolCall };
|
|
77
|
+
}
|
|
78
|
+
async function buildHeaders(source, base) {
|
|
79
|
+
const headers = new Headers(base);
|
|
80
|
+
const resolved = typeof source === "function" ? await source() : source;
|
|
81
|
+
if (resolved) {
|
|
82
|
+
for (const [key, value] of new Headers(resolved)) {
|
|
83
|
+
headers.set(key, value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return headers;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Bridge a server-side yak handler ({@link createYakHandler} /
|
|
90
|
+
* `createNextYakHandler`, e.g. fronting the `@yak-io/trpc` adapter) into a
|
|
91
|
+
* client-side {@link ToolAdapter}, so server-relayed tools merge into the same
|
|
92
|
+
* manifest + `onToolCall` as browser-executed adapters.
|
|
93
|
+
*/
|
|
94
|
+
export function createYakServerAdapter(config = {}) {
|
|
95
|
+
const endpoint = config.endpoint ?? "/api/yak";
|
|
96
|
+
return {
|
|
97
|
+
id: config.id ?? "server",
|
|
98
|
+
getTools: async () => {
|
|
99
|
+
const res = await fetch(endpoint, { headers: await buildHeaders(config.headers) });
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
throw new Error(`Failed to load tools from ${endpoint} (${res.status})`);
|
|
102
|
+
}
|
|
103
|
+
const chatConfig = (await res.json());
|
|
104
|
+
return chatConfig.tools?.tools ?? [];
|
|
105
|
+
},
|
|
106
|
+
execute: async (name, args) => {
|
|
107
|
+
const res = await fetch(endpoint, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: await buildHeaders(config.headers, { "Content-Type": "application/json" }),
|
|
110
|
+
body: JSON.stringify({ name, args }),
|
|
111
|
+
});
|
|
112
|
+
const data = (await res.json().catch(() => ({})));
|
|
113
|
+
if (!res.ok || !data.ok) {
|
|
114
|
+
throw new Error(data.error ?? `Tool "${name}" failed (${res.status})`);
|
|
115
|
+
}
|
|
116
|
+
return data.result;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { RouteManifest } from "./routes.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ToolManifest } from "./tools.js";
|
|
3
3
|
/**
|
|
4
|
-
* Combined configuration for the chatbot including routes
|
|
4
|
+
* Combined configuration for the chatbot including routes and tools.
|
|
5
5
|
*/
|
|
6
6
|
export type ChatConfig = {
|
|
7
7
|
/** Route manifest */
|
|
8
8
|
routes: RouteManifest;
|
|
9
|
-
/** Optional tool manifest (tRPC, GraphQL, etc.) */
|
|
9
|
+
/** Optional tool manifest (tRPC, GraphQL, REST, custom, etc.) */
|
|
10
10
|
tools?: ToolManifest;
|
|
11
|
-
/** Optional schema sources (GraphQL SDL, OpenAPI specs) passed as context to the LLM */
|
|
12
|
-
schemaSources?: SchemaSource[];
|
|
13
11
|
};
|
|
14
12
|
/**
|
|
15
13
|
* Function that provides the chat configuration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,qBAAqB;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EmbedProtocolVersion } from "../version.js";
|
|
2
2
|
import type { RouteManifest } from "./routes.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ToolManifest } from "./tools.js";
|
|
4
4
|
/**
|
|
5
5
|
* Color values for a single mode (light or dark).
|
|
6
6
|
* Applied as CSS variables within the chat UI.
|
|
@@ -88,8 +88,6 @@ export type IframeMessageFromHost = {
|
|
|
88
88
|
theme?: Theme;
|
|
89
89
|
toolManifest?: ToolManifest;
|
|
90
90
|
routeManifest?: RouteManifest;
|
|
91
|
-
/** Schema sources passed as context to the LLM */
|
|
92
|
-
schemaSources?: SchemaSource[];
|
|
93
91
|
/** Chat configuration options */
|
|
94
92
|
options?: ChatOptions;
|
|
95
93
|
/** Logging enabled flag from host (for cross-origin sync) */
|
|
@@ -159,20 +157,6 @@ export type IframeMessageToHost = {
|
|
|
159
157
|
name: string;
|
|
160
158
|
args: unknown;
|
|
161
159
|
};
|
|
162
|
-
} | {
|
|
163
|
-
type: "yak:graphql_schema_call";
|
|
164
|
-
payload: {
|
|
165
|
-
id: string;
|
|
166
|
-
schemaName: string;
|
|
167
|
-
request: GraphQLRequest;
|
|
168
|
-
};
|
|
169
|
-
} | {
|
|
170
|
-
type: "yak:rest_schema_call";
|
|
171
|
-
payload: {
|
|
172
|
-
id: string;
|
|
173
|
-
schemaName: string;
|
|
174
|
-
request: RESTRequest;
|
|
175
|
-
};
|
|
176
160
|
} | {
|
|
177
161
|
type: "yak:redirect";
|
|
178
162
|
payload: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/types/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/types/messaging.ts"],"names":[],"mappings":"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"}
|
package/dist/types/tools.d.ts
CHANGED
|
@@ -2,30 +2,6 @@
|
|
|
2
2
|
* Generic JSON Schema type
|
|
3
3
|
*/
|
|
4
4
|
export type JSONSchema = Record<string, unknown>;
|
|
5
|
-
/**
|
|
6
|
-
* GraphQL schema source for providing API context to the LLM
|
|
7
|
-
*/
|
|
8
|
-
export type GraphQLSchemaSource = {
|
|
9
|
-
/** Unique identifier for this schema (used in callbacks) */
|
|
10
|
-
name: string;
|
|
11
|
-
type: "graphql";
|
|
12
|
-
/** The GraphQL schema in SDL format */
|
|
13
|
-
schema: string;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* OpenAPI spec source for providing API context to the LLM
|
|
17
|
-
*/
|
|
18
|
-
export type OpenAPISchemaSource = {
|
|
19
|
-
/** Unique identifier for this schema (used in callbacks) */
|
|
20
|
-
name: string;
|
|
21
|
-
type: "openapi";
|
|
22
|
-
/** OpenAPI spec as JSON object or YAML string */
|
|
23
|
-
spec: Record<string, unknown> | string;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Union type for schema sources
|
|
27
|
-
*/
|
|
28
|
-
export type SchemaSource = GraphQLSchemaSource | OpenAPISchemaSource;
|
|
29
5
|
/**
|
|
30
6
|
* GraphQL request generated by the LLM
|
|
31
7
|
*/
|
|
@@ -50,24 +26,6 @@ export type RESTRequest = {
|
|
|
50
26
|
/** Request body */
|
|
51
27
|
body?: unknown;
|
|
52
28
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Handler for GraphQL schema tool calls.
|
|
55
|
-
* The LLM generates the query based on the schema context.
|
|
56
|
-
*
|
|
57
|
-
* @param schemaName - The name of the schema (from SchemaSource.name)
|
|
58
|
-
* @param request - The GraphQL request generated by the LLM
|
|
59
|
-
* @returns The result of executing the GraphQL operation
|
|
60
|
-
*/
|
|
61
|
-
export type GraphQLSchemaHandler = (schemaName: string, request: GraphQLRequest) => Promise<unknown>;
|
|
62
|
-
/**
|
|
63
|
-
* Handler for OpenAPI/REST schema tool calls.
|
|
64
|
-
* The LLM generates the request based on the schema context.
|
|
65
|
-
*
|
|
66
|
-
* @param schemaName - The name of the schema (from SchemaSource.name)
|
|
67
|
-
* @param request - The REST request generated by the LLM
|
|
68
|
-
* @returns The result of executing the REST request
|
|
69
|
-
*/
|
|
70
|
-
export type RESTSchemaHandler = (schemaName: string, request: RESTRequest) => Promise<unknown>;
|
|
71
29
|
/**
|
|
72
30
|
* Payload for a tool call request from the iframe
|
|
73
31
|
*/
|
|
@@ -155,4 +113,39 @@ export type ToolCallEvent = {
|
|
|
155
113
|
* Called after every tool call completes, regardless of success/failure.
|
|
156
114
|
*/
|
|
157
115
|
export type ToolCallEventListener = (event: ToolCallEvent) => void;
|
|
116
|
+
/**
|
|
117
|
+
* Client-side tool adapter — the browser-side analogue of the server `ToolSource`.
|
|
118
|
+
*
|
|
119
|
+
* An adapter contributes tool definitions to the single merged manifest injected
|
|
120
|
+
* into the iframe AND knows how to execute the tools it owns. Adapters are composed
|
|
121
|
+
* with {@link createYakToolset} into one manifest + one `onToolCall` funnel, so
|
|
122
|
+
* GraphQL, REST, tRPC (server-relayed via {@link createYakServerAdapter}), and custom
|
|
123
|
+
* tools all flow through the same path — and therefore all surface through
|
|
124
|
+
* `onToolCallComplete` / `useYakToolEvent`.
|
|
125
|
+
*/
|
|
126
|
+
export type ToolAdapter = {
|
|
127
|
+
/** Stable id for diagnostics / manifest source attribution. */
|
|
128
|
+
id?: string;
|
|
129
|
+
/** Tool definitions this adapter contributes to the merged manifest. */
|
|
130
|
+
getTools: () => ToolDefinition[] | Promise<ToolDefinition[]>;
|
|
131
|
+
/** Execute a tool this adapter owns. Same signature as {@link ToolCallHandler}. */
|
|
132
|
+
execute: ToolCallHandler;
|
|
133
|
+
/**
|
|
134
|
+
* Whether this adapter owns `name`. When omitted, ownership defaults to
|
|
135
|
+
* membership in the names returned by {@link ToolAdapter.getTools}.
|
|
136
|
+
*/
|
|
137
|
+
ownsTool?: (name: string) => boolean;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* The result of composing {@link ToolAdapter}s with {@link createYakToolset}:
|
|
141
|
+
* a merged tool manifest for `getConfig()` and a single routed `onToolCall`.
|
|
142
|
+
*/
|
|
143
|
+
export type YakToolset = {
|
|
144
|
+
/** Resolved, merged + deduped tool manifest — spread into `getConfig().tools`. */
|
|
145
|
+
getConfig: () => Promise<{
|
|
146
|
+
tools: ToolManifest;
|
|
147
|
+
}>;
|
|
148
|
+
/** Single routed funnel for `YakProvider.onToolCall` / `YakClientConfig.onToolCall`. */
|
|
149
|
+
onToolCall: ToolCallHandler;
|
|
150
|
+
};
|
|
158
151
|
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/types/tools.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/types/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/voice-machine.d.ts
CHANGED
|
@@ -10,11 +10,24 @@
|
|
|
10
10
|
* delegated to the injected `RealtimeMessageContext` so the function is
|
|
11
11
|
* testable with a plain in-memory mock.
|
|
12
12
|
*/
|
|
13
|
+
/**
|
|
14
|
+
* Lifecycle of a voice session, in order:
|
|
15
|
+
* - `idle` — no session (the starting and stopped state).
|
|
16
|
+
* - `connecting` — establishing the WebRTC connection.
|
|
17
|
+
* - `listening` — connected and capturing the user's speech.
|
|
18
|
+
* - `thinking` — the model is processing / generating a response.
|
|
19
|
+
* - `speaking` — the assistant is playing audio back.
|
|
20
|
+
* - `error` — the session failed; see {@link VoiceMachine.errorMessage}.
|
|
21
|
+
*/
|
|
13
22
|
export type VoiceState = "idle" | "connecting" | "listening" | "thinking" | "speaking" | "error";
|
|
14
23
|
export type VoiceEvent = {
|
|
15
24
|
type: "start";
|
|
16
25
|
} | {
|
|
17
26
|
type: "connected";
|
|
27
|
+
}
|
|
28
|
+
/** An assistant-initiated turn was requested (e.g. the opening greeting). */
|
|
29
|
+
| {
|
|
30
|
+
type: "response_requested";
|
|
18
31
|
} | {
|
|
19
32
|
type: "speech_started";
|
|
20
33
|
} | {
|
|
@@ -29,8 +42,11 @@ export type VoiceEvent = {
|
|
|
29
42
|
type: "error";
|
|
30
43
|
message: string;
|
|
31
44
|
};
|
|
45
|
+
/** Snapshot of a voice session's state machine. */
|
|
32
46
|
export interface VoiceMachine {
|
|
47
|
+
/** Current lifecycle state of the session. */
|
|
33
48
|
state: VoiceState;
|
|
49
|
+
/** Human-readable failure reason — set only when `state === "error"`. */
|
|
34
50
|
errorMessage?: string;
|
|
35
51
|
}
|
|
36
52
|
export declare const INITIAL_VOICE_MACHINE: VoiceMachine;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-machine.d.ts","sourceRoot":"","sources":["../src/voice-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAEjG,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"voice-machine.d.ts","sourceRoot":"","sources":["../src/voice-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAEjG,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE;AACvB,6EAA6E;GAC3E;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,KAAK,EAAE,UAAU,CAAC;IAClB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,YAAgC,CAAC;AAErE,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,YAAY,CAiCnF;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1C,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACtD;AAmID,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,sBAAsB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAmCf"}
|
package/dist/voice-machine.js
CHANGED
|
@@ -17,6 +17,11 @@ export function voiceReducer(machine, event) {
|
|
|
17
17
|
return machine.state === "idle" ? { state: "connecting" } : machine;
|
|
18
18
|
case "connected":
|
|
19
19
|
return machine.state === "connecting" ? { state: "listening" } : machine;
|
|
20
|
+
case "response_requested":
|
|
21
|
+
// The assistant is generating an unprompted turn (the opening greeting).
|
|
22
|
+
// Move to `thinking` so the subsequent `audio_delta` lands on `speaking`,
|
|
23
|
+
// matching a normal turn; no-op if we're not idling in `listening`.
|
|
24
|
+
return machine.state === "listening" ? { state: "thinking" } : machine;
|
|
20
25
|
case "speech_started":
|
|
21
26
|
if (machine.state === "idle" || machine.state === "error")
|
|
22
27
|
return machine;
|
package/dist/voice-session.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChatConfig, ChatConfigProvider } from "./types/config.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ToolCallHandler } from "./types/tools.js";
|
|
3
3
|
import { type VoiceMachine } from "./voice-machine.js";
|
|
4
4
|
declare global {
|
|
5
5
|
interface Window {
|
|
@@ -11,8 +11,6 @@ export interface YakVoiceSessionConfig {
|
|
|
11
11
|
appId: string;
|
|
12
12
|
/** Tool call handler. Same shape as `YakClientConfig.onToolCall`. */
|
|
13
13
|
onToolCall?: ToolCallHandler;
|
|
14
|
-
onGraphQLSchemaCall?: GraphQLSchemaHandler;
|
|
15
|
-
onRESTSchemaCall?: RESTSchemaHandler;
|
|
16
14
|
onRedirect?: (path: string) => void;
|
|
17
15
|
/**
|
|
18
16
|
* Static chat config (routes + tools). Sent to the mint endpoint so the LLM
|
|
@@ -26,8 +24,8 @@ export interface YakVoiceSessionConfig {
|
|
|
26
24
|
*/
|
|
27
25
|
getConfig?: ChatConfigProvider;
|
|
28
26
|
/**
|
|
29
|
-
* Override the API origin
|
|
30
|
-
*
|
|
27
|
+
* Override the API origin (where voice sessions are minted). Defaults to
|
|
28
|
+
* `https://chat.yak.io`. Most integrators never set this.
|
|
31
29
|
*/
|
|
32
30
|
apiOrigin?: string;
|
|
33
31
|
}
|
|
@@ -48,18 +46,16 @@ export declare class YakVoiceSession {
|
|
|
48
46
|
private toolNameById;
|
|
49
47
|
constructor(config: YakVoiceSessionConfig);
|
|
50
48
|
/**
|
|
51
|
-
* Resolve the API origin lazily on each call.
|
|
52
|
-
* (
|
|
53
|
-
*
|
|
54
|
-
* bake in the production URL.
|
|
49
|
+
* Resolve the API origin lazily on each call. Environment-dependent defaults
|
|
50
|
+
* (e.g. a local chat UI) may not be ready at construction time, so resolving
|
|
51
|
+
* eagerly would risk baking in the production URL.
|
|
55
52
|
*/
|
|
56
53
|
private get apiOrigin();
|
|
57
54
|
/** Update mutable config fields (handlers, getConfig). */
|
|
58
55
|
updateConfig(patch: Partial<YakVoiceSessionConfig>): void;
|
|
59
56
|
getState(): VoiceMachine;
|
|
60
57
|
/**
|
|
61
|
-
* The current API origin (`chat.yak.io
|
|
62
|
-
* `http://localhost:3001` when `__YAK_INTERNAL_DEV__` is set). Useful for
|
|
58
|
+
* The current API origin (defaults to `https://chat.yak.io`). Useful for
|
|
63
59
|
* building URLs to static assets like the brand logo.
|
|
64
60
|
*/
|
|
65
61
|
getApiOrigin(): string;
|
|
@@ -85,9 +81,11 @@ export declare class YakVoiceSession {
|
|
|
85
81
|
private execMcpTool;
|
|
86
82
|
private mintToken;
|
|
87
83
|
/**
|
|
88
|
-
* Decorate the host's tool manifest
|
|
89
|
-
*
|
|
90
|
-
*
|
|
84
|
+
* Decorate the host's tool manifest with hashed ids and populate
|
|
85
|
+
* `this.toolNameById` for reverse lookup. Mirrors the decoration the chat-ui
|
|
86
|
+
* iframe applies before sending tools to `/api/chat`. GraphQL/REST tools are
|
|
87
|
+
* ordinary manifest entries here (contributed by their adapters), so no
|
|
88
|
+
* special-casing is needed.
|
|
91
89
|
*/
|
|
92
90
|
private buildDecoratedManifest;
|
|
93
91
|
private exchangeSdp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-session.d.ts","sourceRoot":"","sources":["../src/voice-session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"voice-session.d.ts","sourceRoot":"","sources":["../src/voice-session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAKL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC;CACF;AAiBD,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;AAEjE,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,eAAe,CAA6B;IACpD,6EAA6E;IAC7E,OAAO,CAAC,KAAK,CAAkC;IAC/C;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAA6B;gBAErC,MAAM,EAAE,qBAAqB;IAKzC;;;;OAIG;IACH,OAAO,KAAK,SAAS,GAEpB;IAED,0DAA0D;IACnD,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,IAAI;IAIzD,QAAQ,IAAI,YAAY;IAI/B;;;OAGG;IACI,YAAY,IAAI,MAAM;IAItB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAO9D;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwJnC,oDAAoD;IACvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,2FAA2F;IACpF,OAAO,IAAI,IAAI;IAWtB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,mBAAmB;YAeb,aAAa;IA8B3B;;;;OAIG;YACW,WAAW;YAwBX,SAAS;IAsBvB;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;YAchB,WAAW;IAmBzB,OAAO,CAAC,kBAAkB;YAWZ,gBAAgB;YA2BhB,QAAQ;YAuCR,QAAQ;IAMtB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,cAAc;CAevB"}
|
package/dist/voice-session.js
CHANGED
|
@@ -4,18 +4,18 @@ import { generateToolId } from "./tool-name.js";
|
|
|
4
4
|
import { handleRealtimeMessage, INITIAL_VOICE_MACHINE, voiceReducer, } from "./voice-machine.js";
|
|
5
5
|
const DEFAULT_REALTIME_MODEL = "gpt-realtime";
|
|
6
6
|
const REALTIME_CALLS_URL = "https://api.openai.com/v1/realtime/calls";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
const DEFAULT_API_ORIGIN = "https://chat.yak.io";
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the API origin when no explicit `apiOrigin` is configured. Points at
|
|
10
|
+
* a local chat UI during yak's own local development; production otherwise.
|
|
11
|
+
*/
|
|
12
|
+
function getDefaultApiOrigin() {
|
|
13
|
+
if (typeof window !== "undefined" &&
|
|
14
|
+
(window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") &&
|
|
15
|
+
typeof window.__YAK_INTERNAL_DEV__ !== "undefined") {
|
|
16
|
+
return "http://localhost:3001";
|
|
17
17
|
}
|
|
18
|
-
return
|
|
18
|
+
return DEFAULT_API_ORIGIN;
|
|
19
19
|
}
|
|
20
20
|
const EMPTY_RESOURCES = {
|
|
21
21
|
pc: null,
|
|
@@ -56,13 +56,12 @@ export class YakVoiceSession {
|
|
|
56
56
|
this.attachPageHide();
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Resolve the API origin lazily on each call.
|
|
60
|
-
* (
|
|
61
|
-
*
|
|
62
|
-
* bake in the production URL.
|
|
59
|
+
* Resolve the API origin lazily on each call. Environment-dependent defaults
|
|
60
|
+
* (e.g. a local chat UI) may not be ready at construction time, so resolving
|
|
61
|
+
* eagerly would risk baking in the production URL.
|
|
63
62
|
*/
|
|
64
63
|
get apiOrigin() {
|
|
65
|
-
return this.config.apiOrigin ??
|
|
64
|
+
return this.config.apiOrigin ?? getDefaultApiOrigin();
|
|
66
65
|
}
|
|
67
66
|
/** Update mutable config fields (handlers, getConfig). */
|
|
68
67
|
updateConfig(patch) {
|
|
@@ -72,8 +71,7 @@ export class YakVoiceSession {
|
|
|
72
71
|
return this.machine;
|
|
73
72
|
}
|
|
74
73
|
/**
|
|
75
|
-
* The current API origin (`chat.yak.io
|
|
76
|
-
* `http://localhost:3001` when `__YAK_INTERNAL_DEV__` is set). Useful for
|
|
74
|
+
* The current API origin (defaults to `https://chat.yak.io`). Useful for
|
|
77
75
|
* building URLs to static assets like the brand logo.
|
|
78
76
|
*/
|
|
79
77
|
getApiOrigin() {
|
|
@@ -102,7 +100,6 @@ export class YakVoiceSession {
|
|
|
102
100
|
logger.debug("Voice: getConfig() resolved", {
|
|
103
101
|
toolCount: chatConfig?.tools?.tools.length ?? 0,
|
|
104
102
|
routeCount: chatConfig?.routes?.routes.length ?? 0,
|
|
105
|
-
hasSchemas: !!chatConfig?.schemaSources?.length,
|
|
106
103
|
});
|
|
107
104
|
}
|
|
108
105
|
catch (err) {
|
|
@@ -113,7 +110,6 @@ export class YakVoiceSession {
|
|
|
113
110
|
logger.debug("Voice: using static chatConfig", {
|
|
114
111
|
toolCount: chatConfig.tools?.tools.length ?? 0,
|
|
115
112
|
routeCount: chatConfig.routes?.routes.length ?? 0,
|
|
116
|
-
hasSchemas: !!chatConfig.schemaSources?.length,
|
|
117
113
|
});
|
|
118
114
|
}
|
|
119
115
|
else {
|
|
@@ -197,6 +193,17 @@ export class YakVoiceSession {
|
|
|
197
193
|
dataChannel.onopen = () => {
|
|
198
194
|
logger.debug("Voice: data channel opened");
|
|
199
195
|
this.dispatch({ type: "connected" });
|
|
196
|
+
// Kick off an opening greeting so the assistant speaks first instead of
|
|
197
|
+
// waiting silently for the user. The minted session already carries the
|
|
198
|
+
// full instructions (persona, governance, language, "first turn" rule),
|
|
199
|
+
// so a bare `response.create` is enough — do NOT attach response-level
|
|
200
|
+
// instructions here, which would replace the session instructions.
|
|
201
|
+
// Skip it when the app's voice intro is "none" (`autoGreet === false`);
|
|
202
|
+
// a missing flag defaults to greeting for back-compat.
|
|
203
|
+
if (mint.autoGreet !== false) {
|
|
204
|
+
this.dispatch({ type: "response_requested" });
|
|
205
|
+
this.sendOverDataChannel({ type: "response.create" });
|
|
206
|
+
}
|
|
200
207
|
};
|
|
201
208
|
dataChannel.onclose = () => {
|
|
202
209
|
logger.debug("Voice: data channel closed");
|
|
@@ -313,14 +320,6 @@ export class YakVoiceSession {
|
|
|
313
320
|
}
|
|
314
321
|
return { success: true, redirected: true, path };
|
|
315
322
|
}
|
|
316
|
-
if (name.startsWith("graphql_") && this.config.onGraphQLSchemaCall) {
|
|
317
|
-
const schemaName = name.slice("graphql_".length);
|
|
318
|
-
return await this.config.onGraphQLSchemaCall(schemaName, args);
|
|
319
|
-
}
|
|
320
|
-
if (name.startsWith("rest_") && this.config.onRESTSchemaCall) {
|
|
321
|
-
const schemaName = name.slice("rest_".length);
|
|
322
|
-
return await this.config.onRESTSchemaCall(schemaName, args);
|
|
323
|
-
}
|
|
324
323
|
if (this.config.onToolCall) {
|
|
325
324
|
return await this.config.onToolCall(name, args);
|
|
326
325
|
}
|
|
@@ -364,7 +363,6 @@ export class YakVoiceSession {
|
|
|
364
363
|
pageContext,
|
|
365
364
|
toolManifest: decoratedManifest,
|
|
366
365
|
routeManifest: chatConfig?.routes,
|
|
367
|
-
schemaSources: chatConfig?.schemaSources,
|
|
368
366
|
}),
|
|
369
367
|
});
|
|
370
368
|
if (!res.ok) {
|
|
@@ -374,9 +372,11 @@ export class YakVoiceSession {
|
|
|
374
372
|
return (await res.json());
|
|
375
373
|
}
|
|
376
374
|
/**
|
|
377
|
-
* Decorate the host's tool manifest
|
|
378
|
-
*
|
|
379
|
-
*
|
|
375
|
+
* Decorate the host's tool manifest with hashed ids and populate
|
|
376
|
+
* `this.toolNameById` for reverse lookup. Mirrors the decoration the chat-ui
|
|
377
|
+
* iframe applies before sending tools to `/api/chat`. GraphQL/REST tools are
|
|
378
|
+
* ordinary manifest entries here (contributed by their adapters), so no
|
|
379
|
+
* special-casing is needed.
|
|
380
380
|
*/
|
|
381
381
|
buildDecoratedManifest(chatConfig) {
|
|
382
382
|
this.toolNameById.clear();
|
|
@@ -385,19 +385,7 @@ export class YakVoiceSession {
|
|
|
385
385
|
this.toolNameById.set(id, t.name);
|
|
386
386
|
return { ...t, id };
|
|
387
387
|
});
|
|
388
|
-
|
|
389
|
-
const name = source.type === "graphql" ? `graphql_${source.name}` : `rest_${source.name}`;
|
|
390
|
-
const id = generateToolId(name);
|
|
391
|
-
this.toolNameById.set(id, name);
|
|
392
|
-
return {
|
|
393
|
-
id,
|
|
394
|
-
name,
|
|
395
|
-
description: source.type === "graphql"
|
|
396
|
-
? `Execute GraphQL operations against ${source.name}`
|
|
397
|
-
: `Make REST API calls to ${source.name}`,
|
|
398
|
-
};
|
|
399
|
-
});
|
|
400
|
-
return { tools: [...decoratedHostTools, ...decoratedSchemaTools] };
|
|
388
|
+
return { tools: decoratedHostTools };
|
|
401
389
|
}
|
|
402
390
|
async exchangeSdp(offer, clientSecret) {
|
|
403
391
|
const sdpResponse = await fetch(`${REALTIME_CALLS_URL}?model=${DEFAULT_REALTIME_MODEL}`, {
|