expo-local-llm 0.2.2 → 0.3.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 +122 -26
- package/build/ExpoLocalLlm.types.d.ts +24 -1
- package/build/ExpoLocalLlm.types.d.ts.map +1 -1
- package/build/ExpoLocalLlm.types.js.map +1 -1
- package/build/LLMSession.d.ts.map +1 -1
- package/build/LLMSession.js +7 -0
- package/build/LLMSession.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/useLocalLLM.d.ts +2 -0
- package/build/useLocalLLM.d.ts.map +1 -1
- package/build/useLocalLLM.js +31 -1
- package/build/useLocalLLM.js.map +1 -1
- package/build/validateSchema.d.ts +18 -0
- package/build/validateSchema.d.ts.map +1 -0
- package/build/validateSchema.js +104 -0
- package/build/validateSchema.js.map +1 -0
- package/ios/FoundationModelBridge.swift +34 -2
- package/ios/GenerationSchemaBuilder.swift +67 -0
- package/ios/LLMSession.swift +61 -18
- package/ios/Records.swift +1 -0
- package/package.json +1 -1
- package/src/ExpoLocalLlm.types.ts +20 -1
- package/src/LLMSession.ts +8 -0
- package/src/__tests__/validateSchema.test.ts +218 -0
- package/src/index.ts +6 -0
- package/src/useLocalLLM.ts +34 -1
- package/src/validateSchema.ts +127 -0
package/README.md
CHANGED
|
@@ -13,9 +13,37 @@ Expo module for on-device LLM inference. Wraps Apple Foundation Models (iOS 26+)
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install expo-local-llm
|
|
16
|
-
npx expo prebuild
|
|
17
16
|
```
|
|
18
17
|
|
|
18
|
+
This module requires **iOS 16.0+** as a compile target. The default Expo template targets iOS 15.1, so you'll need to raise it via [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx expo install expo-build-properties
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
In `app.json`, add the plugin with the deployment target:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"expo": {
|
|
29
|
+
"plugins": [
|
|
30
|
+
[
|
|
31
|
+
"expo-build-properties",
|
|
32
|
+
{ "ios": { "deploymentTarget": "16.0" } }
|
|
33
|
+
]
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then prebuild:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx expo prebuild --clean
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> Without `expo-build-properties`, you'll hit `compiling for iOS 15.1, but module 'ExpoLocalLlm' has a minimum deployment target of iOS 16.0` at build time. (Apple Intelligence itself still requires iOS 26+ at runtime — the 16.0 floor is just for compilation; the module returns `notEligible` on iOS 16–25.)
|
|
46
|
+
|
|
19
47
|
## Usage
|
|
20
48
|
|
|
21
49
|
```tsx
|
|
@@ -94,19 +122,71 @@ function WeatherChat() {
|
|
|
94
122
|
|
|
95
123
|
### Structured Output
|
|
96
124
|
|
|
125
|
+
iOS 26+ uses Apple's constrained decoding (`DynamicGenerationSchema`) — output is guaranteed to be
|
|
126
|
+
a structurally-valid JSON object matching the schema. Android falls back to instruction-based
|
|
127
|
+
guidance.
|
|
128
|
+
|
|
129
|
+
<p align="center">
|
|
130
|
+
<img src="https://raw.githubusercontent.com/GijungKim/expo-local-llm/main/docs/structured-output.png" alt="Structured output demo — recipe JSON with constrained 'difficulty' enum on iPhone" width="320" />
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
*Real device output: the model fills in a recipe schema with `difficulty` constrained to `easy | medium | hard`. The streaming demo lives at `example/App.tsx`.*
|
|
134
|
+
|
|
97
135
|
```tsx
|
|
98
136
|
const { respond } = useLocalLLM({
|
|
99
137
|
responseFormat: 'json',
|
|
100
138
|
schema: {
|
|
101
139
|
name: { type: 'string', description: 'Recipe name' },
|
|
102
|
-
ingredients: {
|
|
103
|
-
|
|
140
|
+
ingredients: {
|
|
141
|
+
type: 'array',
|
|
142
|
+
items: { type: 'string' },
|
|
143
|
+
description: 'List of ingredients',
|
|
144
|
+
},
|
|
145
|
+
steps: {
|
|
146
|
+
type: 'array',
|
|
147
|
+
items: { type: 'string' },
|
|
148
|
+
description: 'Cooking steps',
|
|
149
|
+
},
|
|
150
|
+
difficulty: {
|
|
151
|
+
type: 'string',
|
|
152
|
+
enum: ['easy', 'medium', 'hard'],
|
|
153
|
+
},
|
|
104
154
|
},
|
|
105
155
|
});
|
|
106
156
|
|
|
107
157
|
const recipe = JSON.parse(await respond('Give me a pasta recipe'));
|
|
108
158
|
```
|
|
109
159
|
|
|
160
|
+
Streaming with a schema yields progressively-filled snapshots via `streamedJSON` (the current
|
|
161
|
+
partial as a JSON string) and `streamedObject` (the parsed value):
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
const { streamResponse, streamedObject } = useLocalLLM({
|
|
165
|
+
responseFormat: 'json',
|
|
166
|
+
schema: { /* ... */ },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await streamResponse('Give me a pasta recipe');
|
|
170
|
+
// streamedObject updates as the model fills in fields
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Schema validation
|
|
174
|
+
|
|
175
|
+
`createLLMSession` validates the schema before crossing to native and throws `SchemaInvalidError`
|
|
176
|
+
with per-field paths if anything is malformed (missing `items`, `enum` on the wrong type, unknown
|
|
177
|
+
`type`, etc.). You can also call `validateSchema()` directly when building schemas at runtime:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { validateSchema } from 'expo-local-llm';
|
|
181
|
+
|
|
182
|
+
const result = validateSchema(mySchema);
|
|
183
|
+
if (!result.ok) {
|
|
184
|
+
for (const err of result.errors) {
|
|
185
|
+
console.warn(`${err.path}: ${err.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
110
190
|
## API
|
|
111
191
|
|
|
112
192
|
### `useLocalLLM(options?)`
|
|
@@ -122,7 +202,8 @@ const recipe = JSON.parse(await respond('Give me a pasta recipe'));
|
|
|
122
202
|
| `tools` | `ToolDefinition[]` | Tools the model can invoke (iOS 26+ only) |
|
|
123
203
|
| `toolTimeout` | `number` | Seconds before an unresolved tool call times out (default 30) |
|
|
124
204
|
| `responseFormat` | `"text" \| "json"` | Set to `"json"` for structured JSON output |
|
|
125
|
-
| `schema` | `
|
|
205
|
+
| `schema` | `Schema` | Schema for structured output (used with `responseFormat: "json"`) — see Structured Output section |
|
|
206
|
+
| `includeSchemaInPrompt` | `boolean` | iOS 26+: include the schema definition in the prompt. Default `true`. Set `false` when you've front-loaded few-shot examples. |
|
|
126
207
|
|
|
127
208
|
#### Returns
|
|
128
209
|
|
|
@@ -130,7 +211,9 @@ const recipe = JSON.parse(await respond('Give me a pasta recipe'));
|
|
|
130
211
|
|----------|------|-------------|
|
|
131
212
|
| `availability` | `ModelAvailability` | Current model status |
|
|
132
213
|
| `isGenerating` | `boolean` | Whether a generation is in progress |
|
|
133
|
-
| `streamedText` | `string` | Accumulated text from current stream |
|
|
214
|
+
| `streamedText` | `string` | Accumulated text from current text stream |
|
|
215
|
+
| `streamedJSON` | `string` | Current partial JSON from a schema-driven stream |
|
|
216
|
+
| `streamedObject` | `unknown` | Parsed value of `streamedJSON` (or `null` before any partial) |
|
|
134
217
|
| `downloadProgress` | `number \| null` | Download progress 0-1 (Android only) |
|
|
135
218
|
| `error` | `string \| null` | Last error message |
|
|
136
219
|
| `activeToolCalls` | `ActiveToolCall[]` | In-flight tool calls (`{ callId, toolName }`) |
|
|
@@ -161,7 +244,8 @@ const recipe = JSON.parse(await respond('Give me a pasta recipe'));
|
|
|
161
244
|
- **iOS**: Apple's Foundation Model may refuse certain categories of prompts (e.g. personal health data interpretation) due to built-in safety guardrails.
|
|
162
245
|
- Methods (`respond`, `streamResponse`, `cancelStream`) are only defined when `availability === 'available'`.
|
|
163
246
|
- Tool calling is iOS 26+ only. Android will throw at session creation if tools are passed.
|
|
164
|
-
- Structured output uses instruction-based JSON guidance
|
|
247
|
+
- Structured output on Android uses instruction-based JSON guidance. iOS 26+ uses constrained
|
|
248
|
+
decoding via `DynamicGenerationSchema`.
|
|
165
249
|
|
|
166
250
|
## Why an Expo module?
|
|
167
251
|
|
|
@@ -185,33 +269,45 @@ a bare React Native library. Here's why:
|
|
|
185
269
|
If you're on bare React Native without Expo, this module won't work — you'd need
|
|
186
270
|
to add `expo-modules-core` as a dependency or use a different library.
|
|
187
271
|
|
|
188
|
-
## How is this different from
|
|
272
|
+
## How is this different from React Native AI? (as of May 2026)
|
|
189
273
|
|
|
190
|
-
[
|
|
191
|
-
|
|
192
|
-
|
|
274
|
+
[React Native AI](https://github.com/callstackincubator/ai) (formerly `@callstack/ai`)
|
|
275
|
+
is a Vercel AI SDK-compatible collection of on-device AI primitives, modularized
|
|
276
|
+
into per-backend packages: `@react-native-ai/apple`, `@react-native-ai/llama`,
|
|
277
|
+
`@react-native-ai/mlc`. It covers text generation, embeddings, transcription, and
|
|
278
|
+
speech synthesis.
|
|
193
279
|
|
|
194
|
-
`expo-local-llm` takes a
|
|
280
|
+
`expo-local-llm` takes a narrower approach:
|
|
195
281
|
|
|
196
|
-
| | `expo-local-llm` |
|
|
282
|
+
| | `expo-local-llm` | React Native AI |
|
|
197
283
|
|---|---|---|
|
|
198
|
-
| **
|
|
199
|
-
| **iOS model** | Apple Foundation Models (system-provided) | Apple Foundation Models, Llama, MLC |
|
|
200
|
-
| **Android model** | Gemini Nano (system-provided) |
|
|
201
|
-
| **
|
|
202
|
-
| **
|
|
203
|
-
| **
|
|
204
|
-
| **
|
|
205
|
-
| **
|
|
206
|
-
| **Dependencies** | None | Vercel AI SDK
|
|
284
|
+
| **Surface** | React hook (`useLocalLLM`) and a native session object | Vercel AI SDK provider (`generateText`, `streamText`, `embed`, `transcribe`, `speech`) |
|
|
285
|
+
| **iOS model** | Apple Foundation Models (system-provided) | Apple Foundation Models, Llama (via llama.rn), MLC LLM |
|
|
286
|
+
| **Android model** | Gemini Nano (system-provided) | Llama, MLC (no built-in OS model) |
|
|
287
|
+
| **Capabilities** | Text generation, tool calling, constrained JSON output | Text, embeddings, transcription, speech synthesis |
|
|
288
|
+
| **Model management** | None — the OS handles it | Built-in for Apple; download/prepare for Llama/MLC |
|
|
289
|
+
| **Bundle size impact** | Near zero | Depends on which provider you install + model weights |
|
|
290
|
+
| **DevTools** | None | AI SDK Profiler via Rozenite (OpenTelemetry spans) |
|
|
291
|
+
| **Install path** | One `expo install`, autolinks | Per-package install (Apple provider autolinks; Llama/MLC need extra setup) |
|
|
292
|
+
| **Dependencies** | None | Vercel AI SDK (v6) and per-provider runtimes |
|
|
207
293
|
|
|
208
294
|
**Choose `expo-local-llm` if** you want the simplest path to on-device LLM in
|
|
209
|
-
an Expo app and are happy using whatever model the OS provides
|
|
210
|
-
|
|
295
|
+
an Expo app and are happy using whatever model the OS provides (Apple Foundation
|
|
296
|
+
Models on iOS, Gemini Nano on Android). There's nothing to configure, no weights
|
|
297
|
+
to bundle, and it works on both platforms.
|
|
298
|
+
|
|
299
|
+
**Choose React Native AI if** you need any of:
|
|
300
|
+
- Vercel AI SDK compatibility (drop-in replacement for cloud-LLM apps)
|
|
301
|
+
- Capabilities beyond text generation (embeddings, transcription, speech synthesis)
|
|
302
|
+
- Specific models (Llama 3.2, Phi-3, Mistral, Qwen) on either iOS or Android
|
|
303
|
+
- AI SDK Profiler DevTools for tracing
|
|
304
|
+
|
|
305
|
+
## Built with expo-local-llm
|
|
306
|
+
|
|
307
|
+
- [PulseID](https://apps.apple.com/us/app/pulseid-heart-rate-camera/id6754331991) — Heart rate camera
|
|
308
|
+
- [Teamfit Tactics](https://apps.apple.com/us/app/teamfit-tactics-tft-fitness/id6757195493) — TFT fitness
|
|
211
309
|
|
|
212
|
-
|
|
213
|
-
Phi-3, Mistral), want Vercel AI SDK compatibility, or need the generative UI
|
|
214
|
-
and DevTools ecosystem.
|
|
310
|
+
*Using this library? Open a PR to add your app.*
|
|
215
311
|
|
|
216
312
|
## License
|
|
217
313
|
|
|
@@ -31,13 +31,31 @@ export type ActiveToolCall = {
|
|
|
31
31
|
toolName: string;
|
|
32
32
|
};
|
|
33
33
|
export type ResponseFormat = "text" | "json";
|
|
34
|
+
export type SchemaField = {
|
|
35
|
+
type: "string";
|
|
36
|
+
description?: string;
|
|
37
|
+
enum?: string[];
|
|
38
|
+
} | {
|
|
39
|
+
type: "number" | "integer" | "boolean";
|
|
40
|
+
description?: string;
|
|
41
|
+
} | {
|
|
42
|
+
type: "array";
|
|
43
|
+
description?: string;
|
|
44
|
+
items: SchemaField;
|
|
45
|
+
} | {
|
|
46
|
+
type: "object";
|
|
47
|
+
description?: string;
|
|
48
|
+
properties: Record<string, SchemaField>;
|
|
49
|
+
};
|
|
50
|
+
export type Schema = Record<string, SchemaField>;
|
|
34
51
|
export type SessionConfig = {
|
|
35
52
|
instructions?: string;
|
|
36
53
|
options?: GenerationOptions;
|
|
37
54
|
tools?: ToolDefinition[];
|
|
38
55
|
toolTimeout?: number;
|
|
39
56
|
responseFormat?: ResponseFormat;
|
|
40
|
-
schema?:
|
|
57
|
+
schema?: Schema;
|
|
58
|
+
includeSchemaInPrompt?: boolean;
|
|
41
59
|
};
|
|
42
60
|
export type TokenEvent = {
|
|
43
61
|
token: string;
|
|
@@ -46,6 +64,10 @@ export type TokenEvent = {
|
|
|
46
64
|
export type StreamCompleteEvent = {
|
|
47
65
|
text: string;
|
|
48
66
|
};
|
|
67
|
+
export type PartialEvent = {
|
|
68
|
+
json: string;
|
|
69
|
+
complete: boolean;
|
|
70
|
+
};
|
|
49
71
|
export type StreamErrorEvent = {
|
|
50
72
|
error: string;
|
|
51
73
|
};
|
|
@@ -54,6 +76,7 @@ export type DownloadProgress = {
|
|
|
54
76
|
};
|
|
55
77
|
export type LLMSessionEvents = {
|
|
56
78
|
token: (event: TokenEvent) => void;
|
|
79
|
+
partial: (event: PartialEvent) => void;
|
|
57
80
|
streamComplete: (event: StreamCompleteEvent) => void;
|
|
58
81
|
streamError: (event: StreamErrorEvent) => void;
|
|
59
82
|
toolCall: (event: ToolCallEvent) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalLlm.types.d.ts","sourceRoot":"","sources":["../src/ExpoLocalLlm.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GACzB,WAAW,GACX,YAAY,GACZ,UAAU,GACV,aAAa,GACb,kBAAkB,GAClB,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAIF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,iBAAiB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1C,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"ExpoLocalLlm.types.d.ts","sourceRoot":"","sources":["../src/ExpoLocalLlm.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoLocalLlm.types.js","sourceRoot":"","sources":["../src/ExpoLocalLlm.types.ts"],"names":[],"mappings":"","sourcesContent":["export type ModelAvailability =\n | \"available\"\n | \"notEnabled\"\n | \"notReady\"\n | \"notEligible\"\n | \"downloadRequired\"\n | \"downloading\"\n | \"unknown\";\n\nexport type GenerationOptions = {\n temperature?: number;\n maxTokens?: number;\n topK?: number;\n};\n\n// Tool calling types\n\nexport type ToolParameterType = \"string\" | \"number\" | \"boolean\";\n\nexport type ToolParameter = {\n type: ToolParameterType;\n description: string;\n enum?: string[];\n};\n\nexport type ToolDefinition = {\n name: string;\n description: string;\n parameters: Record<string, ToolParameter>;\n handler: (args: Record<string, any>) => Promise<string>;\n};\n\nexport type ToolConfig = {\n name: string;\n description: string;\n parameters: Record<string, ToolParameter>;\n};\n\nexport type ToolCallEvent = {\n callId: string;\n toolName: string;\n arguments: Record<string, any>;\n};\n\nexport type ActiveToolCall = {\n callId: string;\n toolName: string;\n};\n\n// Structured output types\n\nexport type ResponseFormat = \"text\" | \"json\";\n\nexport type SessionConfig = {\n instructions?: string;\n options?: GenerationOptions;\n tools?: ToolDefinition[];\n toolTimeout?: number;\n responseFormat?: ResponseFormat;\n schema?:
|
|
1
|
+
{"version":3,"file":"ExpoLocalLlm.types.js","sourceRoot":"","sources":["../src/ExpoLocalLlm.types.ts"],"names":[],"mappings":"","sourcesContent":["export type ModelAvailability =\n | \"available\"\n | \"notEnabled\"\n | \"notReady\"\n | \"notEligible\"\n | \"downloadRequired\"\n | \"downloading\"\n | \"unknown\";\n\nexport type GenerationOptions = {\n temperature?: number;\n maxTokens?: number;\n topK?: number;\n};\n\n// Tool calling types\n\nexport type ToolParameterType = \"string\" | \"number\" | \"boolean\";\n\nexport type ToolParameter = {\n type: ToolParameterType;\n description: string;\n enum?: string[];\n};\n\nexport type ToolDefinition = {\n name: string;\n description: string;\n parameters: Record<string, ToolParameter>;\n handler: (args: Record<string, any>) => Promise<string>;\n};\n\nexport type ToolConfig = {\n name: string;\n description: string;\n parameters: Record<string, ToolParameter>;\n};\n\nexport type ToolCallEvent = {\n callId: string;\n toolName: string;\n arguments: Record<string, any>;\n};\n\nexport type ActiveToolCall = {\n callId: string;\n toolName: string;\n};\n\n// Structured output types\n\nexport type ResponseFormat = \"text\" | \"json\";\n\nexport type SchemaField =\n | { type: \"string\"; description?: string; enum?: string[] }\n | { type: \"number\" | \"integer\" | \"boolean\"; description?: string }\n | { type: \"array\"; description?: string; items: SchemaField }\n | {\n type: \"object\";\n description?: string;\n properties: Record<string, SchemaField>;\n };\n\nexport type Schema = Record<string, SchemaField>;\n\nexport type SessionConfig = {\n instructions?: string;\n options?: GenerationOptions;\n tools?: ToolDefinition[];\n toolTimeout?: number;\n responseFormat?: ResponseFormat;\n schema?: Schema;\n includeSchemaInPrompt?: boolean;\n};\n\nexport type TokenEvent = {\n token: string;\n accumulated: string;\n};\n\nexport type StreamCompleteEvent = {\n text: string;\n};\n\nexport type PartialEvent = {\n json: string;\n complete: boolean;\n};\n\nexport type StreamErrorEvent = {\n error: string;\n};\n\nexport type DownloadProgress = {\n progress: number;\n};\n\nexport type LLMSessionEvents = {\n token: (event: TokenEvent) => void;\n partial: (event: PartialEvent) => void;\n streamComplete: (event: StreamCompleteEvent) => void;\n streamError: (event: StreamErrorEvent) => void;\n toolCall: (event: ToolCallEvent) => void;\n};\n\nexport type ExpoLocalLlmModuleEvents = {\n downloadProgress: (event: DownloadProgress) => void;\n availabilityChange: (event: { availability: ModelAvailability }) => void;\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMSession.d.ts","sourceRoot":"","sources":["../src/LLMSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACX,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"LLMSession.d.ts","sourceRoot":"","sources":["../src/LLMSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACX,MAAM,sBAAsB,CAAC;AAI9B,qBAAa,UAAW,SAAQ,YAAY,CAAC,gBAAgB,CAAC;IAG5D,OAAO,EAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,cAAc,EAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,YAAY,EAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,EAAG,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,eAAe,EAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,aAAkB,GAAG,UAAU,CAkBvE"}
|
package/build/LLMSession.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SharedObject, UnavailabilityError } from "expo-modules-core";
|
|
2
2
|
import ExpoLocalLlmModule from "./ExpoLocalLlmModule";
|
|
3
|
+
import { validateSchema, SchemaInvalidError } from "./validateSchema";
|
|
3
4
|
export class LLMSession extends SharedObject {
|
|
4
5
|
// Methods are implemented natively via the Class() DSL.
|
|
5
6
|
// TypeScript declarations provide type safety only.
|
|
@@ -15,6 +16,12 @@ export function createLLMSession(config = {}) {
|
|
|
15
16
|
if (!ExpoLocalLlmModule) {
|
|
16
17
|
throw new UnavailabilityError("ExpoLocalLlm", "createLLMSession");
|
|
17
18
|
}
|
|
19
|
+
if (config.schema) {
|
|
20
|
+
const result = validateSchema(config.schema);
|
|
21
|
+
if (!result.ok) {
|
|
22
|
+
throw new SchemaInvalidError(result.errors);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
18
25
|
// Strip handler functions before passing to native — native doesn't need them
|
|
19
26
|
const nativeConfig = {
|
|
20
27
|
...config,
|
package/build/LLMSession.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMSession.js","sourceRoot":"","sources":["../src/LLMSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAOtE,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"LLMSession.js","sourceRoot":"","sources":["../src/LLMSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAOtE,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,MAAM,OAAO,UAAW,SAAQ,YAA8B;IAC5D,wDAAwD;IACxD,oDAAoD;IACpD,OAAO,CAAuC;IAC9C,cAAc,CAAqC;IACnD,YAAY,CAAuB;IACnC,YAAY,CAAgC;IAC5C,cAAc,CAA0B;IACxC,eAAe,CAA4C;IAC3D,cAAc,CAA2C;CAC1D;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAwB,EAAE;IACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,IAAI,mBAAmB,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,MAAM,YAAY,GAAG;QACnB,GAAG,MAAM;QACT,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;KACzD,CAAC;IACF,OAAO,IAAI,kBAAkB,CAAC,UAAU,CAAC,YAAY,CAAe,CAAC;AACvE,CAAC","sourcesContent":["import { SharedObject, UnavailabilityError } from \"expo-modules-core\";\n\nimport type {\n SessionConfig,\n LLMSessionEvents,\n ToolConfig,\n} from \"./ExpoLocalLlm.types\";\nimport ExpoLocalLlmModule from \"./ExpoLocalLlmModule\";\nimport { validateSchema, SchemaInvalidError } from \"./validateSchema\";\n\nexport class LLMSession extends SharedObject<LLMSessionEvents> {\n // Methods are implemented natively via the Class() DSL.\n // TypeScript declarations provide type safety only.\n respond!: (prompt: string) => Promise<string>;\n streamResponse!: (prompt: string) => Promise<void>;\n cancelStream!: () => Promise<void>;\n registerTool!: (config: ToolConfig) => void;\n unregisterTool!: (name: string) => void;\n resolveToolCall!: (callId: string, result: string) => void;\n rejectToolCall!: (callId: string, error: string) => void;\n}\n\nexport function createLLMSession(config: SessionConfig = {}): LLMSession {\n if (!ExpoLocalLlmModule) {\n throw new UnavailabilityError(\"ExpoLocalLlm\", \"createLLMSession\");\n }\n\n if (config.schema) {\n const result = validateSchema(config.schema);\n if (!result.ok) {\n throw new SchemaInvalidError(result.errors);\n }\n }\n\n // Strip handler functions before passing to native — native doesn't need them\n const nativeConfig = {\n ...config,\n tools: config.tools?.map(({ handler, ...rest }) => rest),\n };\n return new ExpoLocalLlmModule.LLMSession(nativeConfig) as LLMSession;\n}\n"]}
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { useLocalLLM } from "./useLocalLLM";
|
|
2
2
|
export { LLMSession, createLLMSession } from "./LLMSession";
|
|
3
3
|
export { default as ExpoLocalLlmModule } from "./ExpoLocalLlmModule";
|
|
4
|
+
export { validateSchema, SchemaInvalidError, type SchemaValidationError, type SchemaValidationResult, } from "./validateSchema";
|
|
4
5
|
export * from "./ExpoLocalLlm.types";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,sBAAsB,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { useLocalLLM } from "./useLocalLLM";
|
|
2
2
|
export { LLMSession, createLLMSession } from "./LLMSession";
|
|
3
3
|
export { default as ExpoLocalLlmModule } from "./ExpoLocalLlmModule";
|
|
4
|
+
export { validateSchema, SchemaInvalidError, } from "./validateSchema";
|
|
4
5
|
export * from "./ExpoLocalLlm.types";
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,cAAc,sBAAsB,CAAC","sourcesContent":["export { useLocalLLM } from \"./useLocalLLM\";\nexport { LLMSession, createLLMSession } from \"./LLMSession\";\nexport { default as ExpoLocalLlmModule } from \"./ExpoLocalLlmModule\";\nexport * from \"./ExpoLocalLlm.types\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,cAAc,EACd,kBAAkB,GAGnB,MAAM,kBAAkB,CAAC;AAC1B,cAAc,sBAAsB,CAAC","sourcesContent":["export { useLocalLLM } from \"./useLocalLLM\";\nexport { LLMSession, createLLMSession } from \"./LLMSession\";\nexport { default as ExpoLocalLlmModule } from \"./ExpoLocalLlmModule\";\nexport {\n validateSchema,\n SchemaInvalidError,\n type SchemaValidationError,\n type SchemaValidationResult,\n} from \"./validateSchema\";\nexport * from \"./ExpoLocalLlm.types\";\n"]}
|
package/build/useLocalLLM.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocalLLM.d.ts","sourceRoot":"","sources":["../src/useLocalLLM.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,
|
|
1
|
+
{"version":3,"file":"useLocalLLM.d.ts","sourceRoot":"","sources":["../src/useLocalLLM.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EAQb,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,KAAK,kBAAkB,GAAG,aAAa,CAAC;AAExC,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,iBAAiB,CAAC;IAChC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC,CAAC;AAEF,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAwRnB"}
|
package/build/useLocalLLM.js
CHANGED
|
@@ -5,6 +5,8 @@ export function useLocalLLM(options = {}) {
|
|
|
5
5
|
const [availability, setAvailability] = useState("unknown");
|
|
6
6
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
7
7
|
const [streamedText, setStreamedText] = useState("");
|
|
8
|
+
const [streamedJSON, setStreamedJSON] = useState("");
|
|
9
|
+
const [streamedObject, setStreamedObject] = useState(null);
|
|
8
10
|
const [downloadProgress, setDownloadProgress] = useState(null);
|
|
9
11
|
const [error, setError] = useState(null);
|
|
10
12
|
const [activeToolCalls, setActiveToolCalls] = useState([]);
|
|
@@ -35,9 +37,11 @@ export function useLocalLLM(options = {}) {
|
|
|
35
37
|
options.options?.topK,
|
|
36
38
|
options.toolTimeout,
|
|
37
39
|
options.responseFormat,
|
|
40
|
+
options.includeSchemaInPrompt,
|
|
38
41
|
schemaFingerprint,
|
|
39
42
|
toolsFingerprint,
|
|
40
43
|
]);
|
|
44
|
+
const isJSONMode = options.responseFormat === "json" && !!options.schema;
|
|
41
45
|
const session = useMemo(() => {
|
|
42
46
|
if (!ExpoLocalLlmModule)
|
|
43
47
|
return null;
|
|
@@ -66,8 +70,28 @@ export function useLocalLLM(options = {}) {
|
|
|
66
70
|
subs.push(session.addListener("token", (event) => {
|
|
67
71
|
setStreamedText(event.accumulated);
|
|
68
72
|
}));
|
|
73
|
+
subs.push(session.addListener("partial", (event) => {
|
|
74
|
+
setStreamedJSON(event.json);
|
|
75
|
+
try {
|
|
76
|
+
setStreamedObject(JSON.parse(event.json));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Partial may not be parseable yet; keep the previous parsed value.
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
69
82
|
subs.push(session.addListener("streamComplete", (event) => {
|
|
70
|
-
|
|
83
|
+
if (isJSONMode) {
|
|
84
|
+
setStreamedJSON(event.text);
|
|
85
|
+
try {
|
|
86
|
+
setStreamedObject(JSON.parse(event.text));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Final JSON should always parse; leave previous value if it doesn't.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
setStreamedText(event.text);
|
|
94
|
+
}
|
|
71
95
|
setIsGenerating(false);
|
|
72
96
|
}));
|
|
73
97
|
subs.push(session.addListener("streamError", (event) => {
|
|
@@ -150,6 +174,8 @@ export function useLocalLLM(options = {}) {
|
|
|
150
174
|
throw new Error("Session not available");
|
|
151
175
|
setError(null);
|
|
152
176
|
setStreamedText("");
|
|
177
|
+
setStreamedJSON("");
|
|
178
|
+
setStreamedObject(null);
|
|
153
179
|
setIsGenerating(true);
|
|
154
180
|
try {
|
|
155
181
|
await session.streamResponse(prompt);
|
|
@@ -177,6 +203,8 @@ export function useLocalLLM(options = {}) {
|
|
|
177
203
|
session: null,
|
|
178
204
|
isGenerating: false,
|
|
179
205
|
streamedText: "",
|
|
206
|
+
streamedJSON: "",
|
|
207
|
+
streamedObject: null,
|
|
180
208
|
downloadProgress: null,
|
|
181
209
|
error: null,
|
|
182
210
|
activeToolCalls: [],
|
|
@@ -187,6 +215,8 @@ export function useLocalLLM(options = {}) {
|
|
|
187
215
|
session,
|
|
188
216
|
isGenerating,
|
|
189
217
|
streamedText,
|
|
218
|
+
streamedJSON,
|
|
219
|
+
streamedObject,
|
|
190
220
|
downloadProgress,
|
|
191
221
|
error,
|
|
192
222
|
activeToolCalls,
|
package/build/useLocalLLM.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocalLLM.js","sourceRoot":"","sources":["../src/useLocalLLM.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAa1E,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAc,MAAM,cAAc,CAAC;AAkB5D,MAAM,UAAU,WAAW,CACzB,UAA8B,EAAE;IAEhC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GACnC,QAAQ,CAAoB,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAmB,EAAE,CAAC,CAAC;IAE7E,gEAAgE;IAChE,2EAA2E;IAC3E,MAAM,eAAe,GAAG,MAAM,CAC5B,IAAI,GAAG,EAAE,CACV,CAAC;IAEF,2CAA2C;IAC3C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqC,CAAC;QAC9D,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBACjC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QACD,eAAe,CAAC,OAAO,GAAG,QAAQ,CAAC;IACrC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAEpB,4DAA4D;IAC5D,sFAAsF;IACtF,MAAM,gBAAgB,GAAG,OAAO,CAC9B,GAAG,EAAE,CACH,OAAO,CAAC,KAAK;QACX,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;SACzE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EACpB,CAAC,OAAO,CAAC,KAAK,CAAC,CAChB,CAAC;IAEF,8DAA8D;IAC9D,MAAM,iBAAiB,GAAG,OAAO,CAC/B,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAC5D,CAAC,OAAO,CAAC,MAAM,CAAC,CACjB,CAAC;IAEF,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,OAAO,EACb;QACE,OAAO,CAAC,YAAY;QACpB,OAAO,CAAC,OAAO,EAAE,WAAW;QAC5B,OAAO,CAAC,OAAO,EAAE,SAAS;QAC1B,OAAO,CAAC,OAAO,EAAE,IAAI;QACrB,OAAO,CAAC,WAAW;QACnB,OAAO,CAAC,cAAc;QACtB,iBAAiB;QACjB,gBAAgB;KACjB,CACF,CAAC;IAEF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE;QAC3B,IAAI,CAAC,kBAAkB;YAAE,OAAO,IAAI,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACzC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,QAAQ,CAAC,CAAC,CAAC,OAAO,IAAI,8BAA8B,CAAC,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,kBAAkB;YAAE,OAAO;QAEhC,eAAe,CAAC,kBAAkB,CAAC,eAAe,EAAuB,CAAC,CAAC;QAE3E,MAAM,IAAI,GAAyB,EAAE,CAAC;QAEtC,IAAI,CAAC,IAAI,CACP,kBAAkB,CAAC,WAAW,CAC5B,kBAAkB,EAClB,CAAC,KAAuB,EAAE,EAAE;YAC1B,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC,CACF,CACF,CAAC;QACF,IAAI,CAAC,IAAI,CACP,kBAAkB,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7D,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC,CAAC,CACH,CAAC;QAEF,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,IAAI,CACP,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,KAAiB,EAAE,EAAE;gBACjD,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACrC,CAAC,CAAC,CACH,CAAC;YACF,IAAI,CAAC,IAAI,CACP,OAAO,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAA0B,EAAE,EAAE;gBACnE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC5B,eAAe,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC,CAAC,CACH,CAAC;YACF,IAAI,CAAC,IAAI,CACP,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,KAAuB,EAAE,EAAE;gBAC7D,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACtB,eAAe,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC,CAAC,CACH,CAAC;YAEF,2BAA2B;YAC3B,IAAI,CAAC,IAAI,CACP,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,KAAoB,EAAE,EAAE;gBACvD,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACtE,kBAAkB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;gBAEpD,MAAM,gBAAgB,GAAG,GAAG,EAAE;oBAC5B,kBAAkB,CAAC,CAAC,IAAI,EAAE,EAAE,CAC1B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CAC9C,CAAC;gBACJ,CAAC,CAAC;gBAEF,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,gBAAgB,EAAE,CAAC;oBACnB,IAAI,CAAC;wBACH,OAAO,CAAC,cAAc,CACpB,KAAK,CAAC,MAAM,EACZ,mCAAmC,KAAK,CAAC,QAAQ,EAAE,CACpD,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,yCAAyC;oBAC3C,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,IAAI,MAAuB,CAAC;gBAC5B,IAAI,CAAC;oBACH,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACpC,CAAC;gBAAC,OAAO,CAAM,EAAE,CAAC;oBAChB,yDAAyD;oBACzD,gBAAgB,EAAE,CAAC;oBACnB,IAAI,CAAC;wBACH,OAAO,CAAC,cAAc,CACpB,KAAK,CAAC,MAAM,EACZ,CAAC,CAAC,OAAO,IAAI,qBAAqB,CACnC,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,yCAAyC;oBAC3C,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,MAAM;qBACH,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;oBACd,gBAAgB,EAAE,CAAC;oBACnB,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAC/C,CAAC,CAAC;qBACD,KAAK,CAAC,CAAC,CAAM,EAAE,EAAE;oBAChB,gBAAgB,EAAE,CAAC;oBACnB,IAAI,CAAC;wBACH,OAAO,CAAC,cAAc,CACpB,KAAK,CAAC,MAAM,EACZ,CAAC,CAAC,OAAO,IAAI,qBAAqB,CACnC,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,yCAAyC;oBAC3C,CAAC;gBACH,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,OAAO,GAAG,WAAW,CACzB,KAAK,EAAE,MAAc,EAAmB,EAAE;QACxC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,eAAe,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7C,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpB,MAAM,CAAC,CAAC;QACV,CAAC;gBAAS,CAAC;YACT,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,EACD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,MAAM,cAAc,GAAG,WAAW,CAChC,KAAK,EAAE,MAAc,EAAiB,EAAE;QACtC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,eAAe,CAAC,EAAE,CAAC,CAAC;QACpB,eAAe,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpB,eAAe,CAAC,KAAK,CAAC,CAAC;YACvB,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC,EACD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACzD,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;QAC7B,eAAe,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QAC1D,IAAI,CAAC,kBAAkB;YAAE,OAAO;QAChC,MAAM,kBAAkB,CAAC,aAAa,EAAE,CAAC;IAC3C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,OAAO;YACL,YAAY,EAAE,aAAa;YAC3B,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,KAAK;YACnB,YAAY,EAAE,EAAE;YAChB,gBAAgB,EAAE,IAAI;YACtB,KAAK,EAAE,IAAI;YACX,eAAe,EAAE,EAAE;SACpB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY;QACZ,OAAO;QACP,YAAY;QACZ,YAAY;QACZ,gBAAgB;QAChB,KAAK;QACL,eAAe;QACf,OAAO,EAAE,OAAO,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACtE,cAAc,EACZ,OAAO,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;QACtE,YAAY,EACV,OAAO,IAAI,YAAY,KAAK,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;QACpE,aAAa;KACd,CAAC;AACJ,CAAC","sourcesContent":["import { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\n\nimport type {\n ModelAvailability,\n SessionConfig,\n TokenEvent,\n StreamCompleteEvent,\n StreamErrorEvent,\n DownloadProgress,\n ToolDefinition,\n ToolCallEvent,\n ActiveToolCall,\n} from \"./ExpoLocalLlm.types\";\nimport ExpoLocalLlmModule from \"./ExpoLocalLlmModule\";\nimport { createLLMSession, LLMSession } from \"./LLMSession\";\n\ntype UseLocalLLMOptions = SessionConfig;\n\ntype UseLocalLLMResult = {\n availability: ModelAvailability;\n session: LLMSession | null;\n isGenerating: boolean;\n streamedText: string;\n downloadProgress: number | null;\n error: string | null;\n activeToolCalls: ActiveToolCall[];\n respond?: (prompt: string) => Promise<string>;\n streamResponse?: (prompt: string) => Promise<void>;\n cancelStream?: () => Promise<void>;\n downloadModel?: () => Promise<void>;\n};\n\nexport function useLocalLLM(\n options: UseLocalLLMOptions = {}\n): UseLocalLLMResult {\n const [availability, setAvailability] =\n useState<ModelAvailability>(\"unknown\");\n const [isGenerating, setIsGenerating] = useState(false);\n const [streamedText, setStreamedText] = useState(\"\");\n const [downloadProgress, setDownloadProgress] = useState<number | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [activeToolCalls, setActiveToolCalls] = useState<ActiveToolCall[]>([]);\n\n // Keep a ref to the current tool handlers so the event listener\n // always sees the latest handlers without needing to recreate the session.\n const toolHandlersRef = useRef<Map<string, ToolDefinition[\"handler\"]>>(\n new Map()\n );\n\n // Update handler ref whenever tools change\n useEffect(() => {\n const handlers = new Map<string, ToolDefinition[\"handler\"]>();\n if (options.tools) {\n for (const tool of options.tools) {\n handlers.set(tool.name, tool.handler);\n }\n }\n toolHandlersRef.current = handlers;\n }, [options.tools]);\n\n // Stabilize options to avoid unnecessary session recreation\n // Include tool names/descriptions so session recreates when tools change structurally\n const toolsFingerprint = useMemo(\n () =>\n options.tools\n ?.map((t) => `${t.name}:${t.description}:${JSON.stringify(t.parameters)}`)\n .join(\"|\") ?? \"\",\n [options.tools]\n );\n\n // Fingerprint the schema so session recreates when it changes\n const schemaFingerprint = useMemo(\n () => (options.schema ? JSON.stringify(options.schema) : \"\"),\n [options.schema]\n );\n\n const stableConfig = useMemo(\n () => options,\n [\n options.instructions,\n options.options?.temperature,\n options.options?.maxTokens,\n options.options?.topK,\n options.toolTimeout,\n options.responseFormat,\n schemaFingerprint,\n toolsFingerprint,\n ]\n );\n\n const session = useMemo(() => {\n if (!ExpoLocalLlmModule) return null;\n try {\n const s = createLLMSession(stableConfig);\n setError(null);\n return s;\n } catch (e: any) {\n setError(e.message ?? \"Failed to create LLM session\");\n return null;\n }\n }, [stableConfig]);\n\n useEffect(() => {\n if (!ExpoLocalLlmModule) return;\n\n setAvailability(ExpoLocalLlmModule.getAvailability() as ModelAvailability);\n\n const subs: { remove(): void }[] = [];\n\n subs.push(\n ExpoLocalLlmModule.addListener(\n \"downloadProgress\",\n (event: DownloadProgress) => {\n setDownloadProgress(event.progress);\n }\n )\n );\n subs.push(\n ExpoLocalLlmModule.addListener(\"availabilityChange\", (event) => {\n setAvailability(event.availability);\n })\n );\n\n if (session) {\n subs.push(\n session.addListener(\"token\", (event: TokenEvent) => {\n setStreamedText(event.accumulated);\n })\n );\n subs.push(\n session.addListener(\"streamComplete\", (event: StreamCompleteEvent) => {\n setStreamedText(event.text);\n setIsGenerating(false);\n })\n );\n subs.push(\n session.addListener(\"streamError\", (event: StreamErrorEvent) => {\n setError(event.error);\n setIsGenerating(false);\n })\n );\n\n // Tool call event listener\n subs.push(\n session.addListener(\"toolCall\", (event: ToolCallEvent) => {\n const activeCall = { callId: event.callId, toolName: event.toolName };\n setActiveToolCalls((prev) => [...prev, activeCall]);\n\n const removeActiveCall = () => {\n setActiveToolCalls((prev) =>\n prev.filter((c) => c.callId !== event.callId)\n );\n };\n\n const handler = toolHandlersRef.current.get(event.toolName);\n if (!handler) {\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n `No handler registered for tool: ${event.toolName}`\n );\n } catch {\n // Ignore if session is already torn down\n }\n return;\n }\n\n let result: Promise<string>;\n try {\n result = handler(event.arguments);\n } catch (e: any) {\n // Handler threw synchronously before returning a promise\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n e.message || \"Tool handler failed\"\n );\n } catch {\n // Ignore if session is already torn down\n }\n return;\n }\n\n result\n .then((value) => {\n removeActiveCall();\n session.resolveToolCall(event.callId, value);\n })\n .catch((e: any) => {\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n e.message || \"Tool handler failed\"\n );\n } catch {\n // Ignore if session is already torn down\n }\n });\n })\n );\n }\n\n return () => {\n subs.forEach((s) => s.remove());\n session?.release();\n };\n }, [session]);\n\n const respond = useCallback(\n async (prompt: string): Promise<string> => {\n if (!session) throw new Error(\"Session not available\");\n setError(null);\n setIsGenerating(true);\n try {\n const result = await session.respond(prompt);\n return result;\n } catch (e: any) {\n setError(e.message);\n throw e;\n } finally {\n setIsGenerating(false);\n }\n },\n [session]\n );\n\n const streamResponse = useCallback(\n async (prompt: string): Promise<void> => {\n if (!session) throw new Error(\"Session not available\");\n setError(null);\n setStreamedText(\"\");\n setIsGenerating(true);\n try {\n await session.streamResponse(prompt);\n } catch (e: any) {\n setError(e.message);\n setIsGenerating(false);\n throw e;\n }\n },\n [session]\n );\n\n const cancelStream = useCallback(async (): Promise<void> => {\n if (!session) return;\n await session.cancelStream();\n setIsGenerating(false);\n }, [session]);\n\n const downloadModel = useCallback(async (): Promise<void> => {\n if (!ExpoLocalLlmModule) return;\n await ExpoLocalLlmModule.downloadModel();\n }, []);\n\n if (!ExpoLocalLlmModule) {\n return {\n availability: \"notEligible\",\n session: null,\n isGenerating: false,\n streamedText: \"\",\n downloadProgress: null,\n error: null,\n activeToolCalls: [],\n };\n }\n\n return {\n availability,\n session,\n isGenerating,\n streamedText,\n downloadProgress,\n error,\n activeToolCalls,\n respond: session && availability === \"available\" ? respond : undefined,\n streamResponse:\n session && availability === \"available\" ? streamResponse : undefined,\n cancelStream:\n session && availability === \"available\" ? cancelStream : undefined,\n downloadModel,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useLocalLLM.js","sourceRoot":"","sources":["../src/useLocalLLM.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\n\nimport type {\n ModelAvailability,\n SessionConfig,\n TokenEvent,\n PartialEvent,\n StreamCompleteEvent,\n StreamErrorEvent,\n DownloadProgress,\n ToolDefinition,\n ToolCallEvent,\n ActiveToolCall,\n} from \"./ExpoLocalLlm.types\";\nimport ExpoLocalLlmModule from \"./ExpoLocalLlmModule\";\nimport { createLLMSession, LLMSession } from \"./LLMSession\";\n\ntype UseLocalLLMOptions = SessionConfig;\n\ntype UseLocalLLMResult = {\n availability: ModelAvailability;\n session: LLMSession | null;\n isGenerating: boolean;\n streamedText: string;\n streamedJSON: string;\n streamedObject: unknown;\n downloadProgress: number | null;\n error: string | null;\n activeToolCalls: ActiveToolCall[];\n respond?: (prompt: string) => Promise<string>;\n streamResponse?: (prompt: string) => Promise<void>;\n cancelStream?: () => Promise<void>;\n downloadModel?: () => Promise<void>;\n};\n\nexport function useLocalLLM(\n options: UseLocalLLMOptions = {}\n): UseLocalLLMResult {\n const [availability, setAvailability] =\n useState<ModelAvailability>(\"unknown\");\n const [isGenerating, setIsGenerating] = useState(false);\n const [streamedText, setStreamedText] = useState(\"\");\n const [streamedJSON, setStreamedJSON] = useState(\"\");\n const [streamedObject, setStreamedObject] = useState<unknown>(null);\n const [downloadProgress, setDownloadProgress] = useState<number | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [activeToolCalls, setActiveToolCalls] = useState<ActiveToolCall[]>([]);\n\n // Keep a ref to the current tool handlers so the event listener\n // always sees the latest handlers without needing to recreate the session.\n const toolHandlersRef = useRef<Map<string, ToolDefinition[\"handler\"]>>(\n new Map()\n );\n\n // Update handler ref whenever tools change\n useEffect(() => {\n const handlers = new Map<string, ToolDefinition[\"handler\"]>();\n if (options.tools) {\n for (const tool of options.tools) {\n handlers.set(tool.name, tool.handler);\n }\n }\n toolHandlersRef.current = handlers;\n }, [options.tools]);\n\n // Stabilize options to avoid unnecessary session recreation\n // Include tool names/descriptions so session recreates when tools change structurally\n const toolsFingerprint = useMemo(\n () =>\n options.tools\n ?.map((t) => `${t.name}:${t.description}:${JSON.stringify(t.parameters)}`)\n .join(\"|\") ?? \"\",\n [options.tools]\n );\n\n // Fingerprint the schema so session recreates when it changes\n const schemaFingerprint = useMemo(\n () => (options.schema ? JSON.stringify(options.schema) : \"\"),\n [options.schema]\n );\n\n const stableConfig = useMemo(\n () => options,\n [\n options.instructions,\n options.options?.temperature,\n options.options?.maxTokens,\n options.options?.topK,\n options.toolTimeout,\n options.responseFormat,\n options.includeSchemaInPrompt,\n schemaFingerprint,\n toolsFingerprint,\n ]\n );\n\n const isJSONMode = options.responseFormat === \"json\" && !!options.schema;\n\n const session = useMemo(() => {\n if (!ExpoLocalLlmModule) return null;\n try {\n const s = createLLMSession(stableConfig);\n setError(null);\n return s;\n } catch (e: any) {\n setError(e.message ?? \"Failed to create LLM session\");\n return null;\n }\n }, [stableConfig]);\n\n useEffect(() => {\n if (!ExpoLocalLlmModule) return;\n\n setAvailability(ExpoLocalLlmModule.getAvailability() as ModelAvailability);\n\n const subs: { remove(): void }[] = [];\n\n subs.push(\n ExpoLocalLlmModule.addListener(\n \"downloadProgress\",\n (event: DownloadProgress) => {\n setDownloadProgress(event.progress);\n }\n )\n );\n subs.push(\n ExpoLocalLlmModule.addListener(\"availabilityChange\", (event) => {\n setAvailability(event.availability);\n })\n );\n\n if (session) {\n subs.push(\n session.addListener(\"token\", (event: TokenEvent) => {\n setStreamedText(event.accumulated);\n })\n );\n subs.push(\n session.addListener(\"partial\", (event: PartialEvent) => {\n setStreamedJSON(event.json);\n try {\n setStreamedObject(JSON.parse(event.json));\n } catch {\n // Partial may not be parseable yet; keep the previous parsed value.\n }\n })\n );\n subs.push(\n session.addListener(\"streamComplete\", (event: StreamCompleteEvent) => {\n if (isJSONMode) {\n setStreamedJSON(event.text);\n try {\n setStreamedObject(JSON.parse(event.text));\n } catch {\n // Final JSON should always parse; leave previous value if it doesn't.\n }\n } else {\n setStreamedText(event.text);\n }\n setIsGenerating(false);\n })\n );\n subs.push(\n session.addListener(\"streamError\", (event: StreamErrorEvent) => {\n setError(event.error);\n setIsGenerating(false);\n })\n );\n\n // Tool call event listener\n subs.push(\n session.addListener(\"toolCall\", (event: ToolCallEvent) => {\n const activeCall = { callId: event.callId, toolName: event.toolName };\n setActiveToolCalls((prev) => [...prev, activeCall]);\n\n const removeActiveCall = () => {\n setActiveToolCalls((prev) =>\n prev.filter((c) => c.callId !== event.callId)\n );\n };\n\n const handler = toolHandlersRef.current.get(event.toolName);\n if (!handler) {\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n `No handler registered for tool: ${event.toolName}`\n );\n } catch {\n // Ignore if session is already torn down\n }\n return;\n }\n\n let result: Promise<string>;\n try {\n result = handler(event.arguments);\n } catch (e: any) {\n // Handler threw synchronously before returning a promise\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n e.message || \"Tool handler failed\"\n );\n } catch {\n // Ignore if session is already torn down\n }\n return;\n }\n\n result\n .then((value) => {\n removeActiveCall();\n session.resolveToolCall(event.callId, value);\n })\n .catch((e: any) => {\n removeActiveCall();\n try {\n session.rejectToolCall(\n event.callId,\n e.message || \"Tool handler failed\"\n );\n } catch {\n // Ignore if session is already torn down\n }\n });\n })\n );\n }\n\n return () => {\n subs.forEach((s) => s.remove());\n session?.release();\n };\n }, [session]);\n\n const respond = useCallback(\n async (prompt: string): Promise<string> => {\n if (!session) throw new Error(\"Session not available\");\n setError(null);\n setIsGenerating(true);\n try {\n const result = await session.respond(prompt);\n return result;\n } catch (e: any) {\n setError(e.message);\n throw e;\n } finally {\n setIsGenerating(false);\n }\n },\n [session]\n );\n\n const streamResponse = useCallback(\n async (prompt: string): Promise<void> => {\n if (!session) throw new Error(\"Session not available\");\n setError(null);\n setStreamedText(\"\");\n setStreamedJSON(\"\");\n setStreamedObject(null);\n setIsGenerating(true);\n try {\n await session.streamResponse(prompt);\n } catch (e: any) {\n setError(e.message);\n setIsGenerating(false);\n throw e;\n }\n },\n [session]\n );\n\n const cancelStream = useCallback(async (): Promise<void> => {\n if (!session) return;\n await session.cancelStream();\n setIsGenerating(false);\n }, [session]);\n\n const downloadModel = useCallback(async (): Promise<void> => {\n if (!ExpoLocalLlmModule) return;\n await ExpoLocalLlmModule.downloadModel();\n }, []);\n\n if (!ExpoLocalLlmModule) {\n return {\n availability: \"notEligible\",\n session: null,\n isGenerating: false,\n streamedText: \"\",\n streamedJSON: \"\",\n streamedObject: null,\n downloadProgress: null,\n error: null,\n activeToolCalls: [],\n };\n }\n\n return {\n availability,\n session,\n isGenerating,\n streamedText,\n streamedJSON,\n streamedObject,\n downloadProgress,\n error,\n activeToolCalls,\n respond: session && availability === \"available\" ? respond : undefined,\n streamResponse:\n session && availability === \"available\" ? streamResponse : undefined,\n cancelStream:\n session && availability === \"available\" ? cancelStream : undefined,\n downloadModel,\n };\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Schema } from "./ExpoLocalLlm.types";
|
|
2
|
+
export type SchemaValidationError = {
|
|
3
|
+
path: string;
|
|
4
|
+
message: string;
|
|
5
|
+
};
|
|
6
|
+
export type SchemaValidationResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
errors: SchemaValidationError[];
|
|
11
|
+
};
|
|
12
|
+
export declare class SchemaInvalidError extends Error {
|
|
13
|
+
readonly errors: SchemaValidationError[];
|
|
14
|
+
constructor(errors: SchemaValidationError[]);
|
|
15
|
+
}
|
|
16
|
+
export declare function validateSchema(schema: unknown): SchemaValidationResult;
|
|
17
|
+
export type { Schema };
|
|
18
|
+
//# sourceMappingURL=validateSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateSchema.d.ts","sourceRoot":"","sources":["../src/validateSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAEnD,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC;gBAC7B,MAAM,EAAE,qBAAqB,EAAE;CAM5C;AAqFD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,sBAAsB,CAmBtE;AAGD,YAAY,EAAE,MAAM,EAAE,CAAC"}
|