@llmrtc/llmrtc-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Tool Types and Registry for LLM Tool Calling
3
+ *
4
+ * Provides provider-agnostic tool definitions that can be adapted
5
+ * to any LLM provider's tool calling format.
6
+ */
7
+ // =============================================================================
8
+ // Tool Registry
9
+ // =============================================================================
10
+ /**
11
+ * Registry for managing tool definitions and handlers
12
+ * Provides lookup by name and bulk operations
13
+ */
14
+ export class ToolRegistry {
15
+ constructor() {
16
+ Object.defineProperty(this, "tools", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: new Map()
21
+ });
22
+ }
23
+ /**
24
+ * Register a tool with its handler
25
+ * @throws Error if a tool with the same name is already registered
26
+ */
27
+ register(tool) {
28
+ const name = tool.definition.name;
29
+ if (this.tools.has(name)) {
30
+ throw new Error(`Tool '${name}' is already registered`);
31
+ }
32
+ this.tools.set(name, tool);
33
+ return this;
34
+ }
35
+ /**
36
+ * Register multiple tools at once
37
+ * @throws Error if any tool name conflicts
38
+ */
39
+ registerAll(tools) {
40
+ for (const tool of tools) {
41
+ this.register(tool);
42
+ }
43
+ return this;
44
+ }
45
+ /**
46
+ * Unregister a tool by name
47
+ * @returns true if the tool was found and removed
48
+ */
49
+ unregister(name) {
50
+ return this.tools.delete(name);
51
+ }
52
+ /**
53
+ * Get a tool by name
54
+ * @returns The tool or undefined if not found
55
+ */
56
+ get(name) {
57
+ return this.tools.get(name);
58
+ }
59
+ /**
60
+ * Check if a tool is registered
61
+ */
62
+ has(name) {
63
+ return this.tools.has(name);
64
+ }
65
+ /**
66
+ * Get all registered tool names
67
+ */
68
+ names() {
69
+ return Array.from(this.tools.keys());
70
+ }
71
+ /**
72
+ * Get tool definitions for a subset of tools
73
+ * Useful for providing stage-specific tool lists
74
+ * @param names - Optional filter; if omitted, returns all definitions
75
+ */
76
+ getDefinitions(names) {
77
+ if (names) {
78
+ return names
79
+ .map(name => this.tools.get(name)?.definition)
80
+ .filter((def) => def !== undefined);
81
+ }
82
+ return Array.from(this.tools.values()).map(t => t.definition);
83
+ }
84
+ /**
85
+ * Get all registered tools
86
+ */
87
+ getAll() {
88
+ return Array.from(this.tools.values());
89
+ }
90
+ /**
91
+ * Clear all registered tools
92
+ */
93
+ clear() {
94
+ this.tools.clear();
95
+ }
96
+ /**
97
+ * Number of registered tools
98
+ */
99
+ get size() {
100
+ return this.tools.size;
101
+ }
102
+ }
103
+ // =============================================================================
104
+ // Helper Functions
105
+ // =============================================================================
106
+ /**
107
+ * Create a tool definition with a handler
108
+ * Type-safe helper for defining tools
109
+ */
110
+ export function defineTool(definition, handler) {
111
+ return { definition, handler };
112
+ }
113
+ /**
114
+ * Validate that arguments match a tool's parameter schema
115
+ * Basic validation - providers typically do this, but useful for testing
116
+ */
117
+ export function validateToolArguments(definition, args) {
118
+ const errors = [];
119
+ const { properties, required = [] } = definition.parameters;
120
+ // Check required parameters
121
+ for (const name of required) {
122
+ if (!(name in args)) {
123
+ errors.push(`Missing required parameter: ${name}`);
124
+ }
125
+ }
126
+ // Check property types
127
+ for (const [name, value] of Object.entries(args)) {
128
+ const schema = properties[name];
129
+ if (!schema) {
130
+ if (definition.parameters.additionalProperties === false) {
131
+ errors.push(`Unknown parameter: ${name}`);
132
+ }
133
+ continue;
134
+ }
135
+ // Basic type checking
136
+ const valueType = Array.isArray(value) ? 'array' : typeof value;
137
+ if (value === null) {
138
+ if (schema.type !== 'null') {
139
+ errors.push(`Parameter '${name}' cannot be null`);
140
+ }
141
+ }
142
+ else if (schema.type === 'integer') {
143
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
144
+ errors.push(`Parameter '${name}' must be an integer`);
145
+ }
146
+ }
147
+ else if (valueType !== schema.type) {
148
+ errors.push(`Parameter '${name}' must be of type ${schema.type}, got ${valueType}`);
149
+ }
150
+ // Enum validation
151
+ if (schema.enum && !schema.enum.includes(value)) {
152
+ errors.push(`Parameter '${name}' must be one of: ${schema.enum.join(', ')}`);
153
+ }
154
+ }
155
+ return { valid: errors.length === 0, errors };
156
+ }
157
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,213 @@
1
+ import type { ToolDefinition, ToolCallRequest, ToolChoice } from './tools.js';
2
+ export type Role = 'system' | 'user' | 'assistant' | 'tool';
3
+ export interface VisionAttachment {
4
+ /** base64-encoded image (data URI) or remote URL depending on provider */
5
+ data: string;
6
+ mimeType?: string;
7
+ alt?: string;
8
+ }
9
+ export interface Message {
10
+ role: Role;
11
+ content: string;
12
+ attachments?: VisionAttachment[];
13
+ /** Tool call ID (for role='tool' messages containing tool results) */
14
+ toolCallId?: string;
15
+ /** Tool name (for role='tool' messages) */
16
+ toolName?: string;
17
+ /** Tool calls made by assistant (for role='assistant' messages that called tools) */
18
+ toolCalls?: ToolCallRequest[];
19
+ }
20
+ export interface SessionConfig {
21
+ systemPrompt?: string;
22
+ historyLimit?: number;
23
+ temperature?: number;
24
+ topP?: number;
25
+ maxTokens?: number;
26
+ }
27
+ export interface LLMRequest {
28
+ messages: Message[];
29
+ config?: SessionConfig;
30
+ stream?: boolean;
31
+ /** Tool definitions available for this request */
32
+ tools?: ToolDefinition[];
33
+ /** How the LLM should choose tools */
34
+ toolChoice?: ToolChoice;
35
+ }
36
+ export interface LLMChunk {
37
+ content: string;
38
+ done: boolean;
39
+ raw?: unknown;
40
+ /** Tool calls (only in final chunk when done=true and stopReason='tool_use') */
41
+ toolCalls?: ToolCallRequest[];
42
+ /** Stop reason (only in final chunk when done=true) */
43
+ stopReason?: StopReason;
44
+ }
45
+ /** Why the LLM stopped generating */
46
+ export type StopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence';
47
+ export interface LLMResult {
48
+ fullText: string;
49
+ raw?: unknown;
50
+ /** Tool calls requested by the LLM (if stopReason is 'tool_use') */
51
+ toolCalls?: ToolCallRequest[];
52
+ /** Why generation stopped */
53
+ stopReason?: StopReason;
54
+ }
55
+ export interface LLMProvider {
56
+ name: string;
57
+ init?(): Promise<void> | void;
58
+ complete(request: LLMRequest): Promise<LLMResult>;
59
+ stream?(request: LLMRequest): AsyncIterable<LLMChunk>;
60
+ }
61
+ export interface STTConfig {
62
+ language?: string;
63
+ interimResults?: boolean;
64
+ model?: string;
65
+ }
66
+ export interface STTResult {
67
+ text: string;
68
+ isFinal: boolean;
69
+ confidence?: number;
70
+ raw?: unknown;
71
+ }
72
+ export interface STTProvider {
73
+ name: string;
74
+ init?(): Promise<void> | void;
75
+ /** One-shot transcription */
76
+ transcribe(audio: Buffer, config?: STTConfig): Promise<STTResult>;
77
+ /** Streaming partials/finals */
78
+ transcribeStream?(audio: AsyncIterable<Buffer>, config?: STTConfig): AsyncIterable<STTResult>;
79
+ }
80
+ export interface TTSConfig {
81
+ voice?: string;
82
+ format?: 'mp3' | 'ogg' | 'wav' | 'pcm';
83
+ model?: string;
84
+ }
85
+ export interface TTSResult {
86
+ audio: Buffer;
87
+ format: TTSConfig['format'];
88
+ raw?: unknown;
89
+ }
90
+ export interface TTSProvider {
91
+ name: string;
92
+ init?(): Promise<void> | void;
93
+ speak(text: string, config?: TTSConfig): Promise<TTSResult>;
94
+ speakStream?(text: string, config?: TTSConfig): AsyncIterable<Buffer>;
95
+ }
96
+ export interface VisionRequest {
97
+ prompt: string;
98
+ attachments: VisionAttachment[];
99
+ stream?: boolean;
100
+ }
101
+ export interface VisionChunk {
102
+ content: string;
103
+ done: boolean;
104
+ raw?: unknown;
105
+ }
106
+ export interface VisionResult {
107
+ content: string;
108
+ raw?: unknown;
109
+ }
110
+ export interface VisionProvider {
111
+ name: string;
112
+ init?(): Promise<void> | void;
113
+ describe(request: VisionRequest): Promise<VisionResult>;
114
+ stream?(request: VisionRequest): AsyncIterable<VisionChunk>;
115
+ }
116
+ export interface TurnCredentials {
117
+ urls: string[];
118
+ username?: string;
119
+ credential?: string;
120
+ }
121
+ export interface SignallingMessage {
122
+ type: 'offer' | 'answer' | 'ice' | 'bye' | 'ping' | 'pong';
123
+ payload?: unknown;
124
+ }
125
+ export interface Logger {
126
+ debug: (...args: unknown[]) => void;
127
+ info: (...args: unknown[]) => void;
128
+ warn: (...args: unknown[]) => void;
129
+ error: (...args: unknown[]) => void;
130
+ }
131
+ export interface TransportEvents {
132
+ onTranscript?: (result: STTResult) => void;
133
+ onLLMChunk?: (chunk: LLMChunk) => void;
134
+ onLLMResult?: (result: LLMResult) => void;
135
+ onTTS?: (audio: TTSResult) => void;
136
+ }
137
+ export interface ConversationProviders {
138
+ llm: LLMProvider;
139
+ stt: STTProvider;
140
+ tts: TTSProvider;
141
+ vision?: VisionProvider;
142
+ }
143
+ export interface ConversationOrchestratorConfig extends SessionConfig {
144
+ providers: ConversationProviders;
145
+ logger?: Logger;
146
+ /** Enable streaming TTS with sentence-boundary detection (default: true) */
147
+ streamingTTS?: boolean;
148
+ /** Optional session ID for hook context */
149
+ sessionId?: string;
150
+ }
151
+ /**
152
+ * Signal types for coordinating audio capture between client and server
153
+ * when using MediaStreamTrack-based audio transport
154
+ */
155
+ export type AudioSignalType = 'audio-start' | 'audio-stop' | 'audio-process';
156
+ /**
157
+ * Sent when VAD detects speech start - server begins buffering audio
158
+ */
159
+ export interface AudioStartSignal {
160
+ type: 'audio-start';
161
+ timestamp: number;
162
+ }
163
+ /**
164
+ * Sent when VAD detects speech end - server stops buffering (without processing)
165
+ */
166
+ export interface AudioStopSignal {
167
+ type: 'audio-stop';
168
+ timestamp: number;
169
+ }
170
+ /**
171
+ * Sent when VAD detects speech end and audio should be processed
172
+ * Includes any vision attachments captured during speech
173
+ */
174
+ export interface AudioProcessSignal {
175
+ type: 'audio-process';
176
+ timestamp: number;
177
+ attachments: VisionAttachment[];
178
+ }
179
+ /**
180
+ * Union type for all audio signals
181
+ */
182
+ export type AudioSignal = AudioStartSignal | AudioStopSignal | AudioProcessSignal;
183
+ /**
184
+ * A chunk of TTS audio data during streaming
185
+ * Yielded by orchestrator as audio becomes available
186
+ */
187
+ export interface TTSChunk {
188
+ type: 'tts-chunk';
189
+ /** Raw audio data (PCM or compressed) */
190
+ audio: Buffer;
191
+ /** Audio format: 'pcm' for raw 16-bit samples, or 'mp3'/'ogg' for compressed */
192
+ format: 'pcm' | 'mp3' | 'ogg' | 'wav';
193
+ /** Sample rate in Hz (e.g., 24000 for OpenAI PCM, 48000 for WebRTC) */
194
+ sampleRate?: number;
195
+ /** The sentence/text this chunk corresponds to */
196
+ sentence?: string;
197
+ }
198
+ /**
199
+ * Signal that all TTS audio has been sent
200
+ */
201
+ export interface TTSComplete {
202
+ type: 'tts-complete';
203
+ }
204
+ /**
205
+ * Signal that TTS streaming is starting
206
+ */
207
+ export interface TTSStart {
208
+ type: 'tts-start';
209
+ }
210
+ /**
211
+ * Union type for all orchestrator yield values during a conversation turn
212
+ */
213
+ export type OrchestratorYield = STTResult | LLMChunk | LLMResult | TTSResult | TTSChunk | TTSStart | TTSComplete;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@llmrtc/llmrtc-core",
3
+ "version": "1.0.0",
4
+ "description": "Core contracts and orchestrators for @llmrtc/LLMRTC",
5
+ "homepage": "https://www.llmrtc.org",
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/llmrtc/llmrtc.git",
19
+ "directory": "packages/core"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/llmrtc/llmrtc/issues",
23
+ "email": "contact@llmrtc.org"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc -b",
35
+ "clean": "rimraf dist",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "dependencies": {},
39
+ "devDependencies": {}
40
+ }