vue-ai-hooks 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hexinmiao96
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY EVENT CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # vue-ai-hooks
2
+
3
+ [English](./README.md) | [็ฎ€ไฝ“ไธญๆ–‡](./README.zh-CN.md)
4
+
5
+ > Vue 3 Composable library for building AI-powered applications.
6
+ > Streaming-first, multi-provider, fully typed.
7
+
8
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![Vue 3](https://img.shields.io/badge/vue-3.4+-42b883.svg)](https://vuejs.org)
10
+ [![TypeScript](https://img.shields.io/badge/typescript-strict-3178c6.svg)](https://www.typescriptlang.org)
11
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
12
+
13
+ `vue-ai-hooks` brings the same DX you'd expect from [VueUse](https://vueuse.org) or
14
+ [Axios](https://axios-http.com) to the LLM world. Three composables, pluggable
15
+ providers, Server-Sent Events streaming handled for you. Works with OpenAI and any
16
+ OpenAI-compatible service (DeepSeek, Moonshot, Zhipu, Ollama via its OpenAI shim,
17
+ vLLM, etc.).
18
+
19
+ ```ts
20
+ import { useChat, openai } from 'vue-ai-hooks'
21
+
22
+ const { messages, input, append, isLoading, stop } = useChat({
23
+ provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
24
+ })
25
+ ```
26
+
27
+ ## Why
28
+
29
+ The AI-in-Vue story is currently fragmented. Options today:
30
+
31
+ | Library | Tradeoff |
32
+ |---|---|
33
+ | **Vercel AI SDK** | React-first; Vue support is unofficial and lags behind |
34
+ | **LangChain.js** | Powerful but heavy; opinionated chains, lots of magic |
35
+ | **Direct fetch + manual SSE** | Works, but you re-implement aborts, retries, and state for every project |
36
+ | **vue-ai-hooks** | A focused, framework-native SDK with the boring parts done |
37
+
38
+ ## Features
39
+
40
+ - ๐ŸŽฏ **Three composables, one mental model** โ€” `useChat`, `useCompletion`, `useEmbedding`
41
+ - ๐ŸŒŠ **Streaming by default** โ€” SSE parsing, AbortController, and reactivity handled for you
42
+ - ๐Ÿ”Œ **Multi-provider** โ€” OpenAI, Azure OpenAI, DeepSeek, Moonshot, Zhipu, Ollama, vLLM, any OpenAI-compatible API
43
+ - ๐Ÿงฐ **Tool calling helpers** โ€” register local handlers and let `useChat` continue the model round-trip
44
+ - ๐Ÿ›  **TypeScript first** โ€” strict mode, no `any` leaks, full IDE autocomplete
45
+ - โšก **Tiny** โ€” zero runtime deps beyond Vue itself
46
+ - ๐Ÿงช **Tested** โ€” Vitest + happy-dom, with fake providers you can copy
47
+ - ๐Ÿ“ฆ **Tree-shakable** โ€” ESM and CJS, named exports, no side effects
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pnpm add vue-ai-hooks
53
+ # or
54
+ npm install vue-ai-hooks
55
+ # or
56
+ yarn add vue-ai-hooks
57
+ ```
58
+
59
+ Peer dependency: `vue@^3.4.0`.
60
+
61
+ ## Quick start
62
+
63
+ ### Streaming chat
64
+
65
+ ```vue
66
+ <script setup lang="ts">
67
+ import { useChat, openai } from 'vue-ai-hooks'
68
+
69
+ const { messages, input, append, isLoading, stop, error } = useChat({
70
+ provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
71
+ })
72
+ </script>
73
+
74
+ <template>
75
+ <div v-for="m in messages" :key="m.id" :class="m.role">
76
+ {{ m.content }}
77
+ </div>
78
+ <textarea v-model="input" />
79
+ <button :disabled="isLoading" @click="append(input); input = ''">Send</button>
80
+ <button :disabled="!isLoading" @click="stop">Stop</button>
81
+ </template>
82
+ ```
83
+
84
+ ### Single-shot completion
85
+
86
+ ```ts
87
+ import { useCompletion, openai } from 'vue-ai-hooks'
88
+
89
+ const { completion, complete } = useCompletion({
90
+ provider: openai({ apiKey: '...' })
91
+ })
92
+
93
+ await complete('Write a haiku about TypeScript:')
94
+ ```
95
+
96
+ ### Embeddings
97
+
98
+ ```ts
99
+ import { useEmbedding, openai } from 'vue-ai-hooks'
100
+
101
+ const { embed, embeddings } = useEmbedding({
102
+ provider: openai({ apiKey: '...' })
103
+ })
104
+
105
+ const result = await embed(['hello world', 'goodbye world'])
106
+ console.log(result.embeddings) // number[][]
107
+ ```
108
+
109
+ ## Using a non-OpenAI provider
110
+
111
+ Every provider implements the same `ChatProvider` interface, so the hooks
112
+ don't care which model is on the other end:
113
+
114
+ ```ts
115
+ import { useChat, openaiCompatible } from 'vue-ai-hooks'
116
+
117
+ useChat({
118
+ provider: openaiCompatible({
119
+ apiKey: 'sk-...',
120
+ baseURL: 'https://api.deepseek.com/v1'
121
+ })
122
+ })
123
+ ```
124
+
125
+ Add a new provider in one file by implementing `ChatProvider`:
126
+
127
+ ```ts
128
+ // src/providers/anthropic.ts
129
+ import type { ChatProvider } from 'vue-ai-hooks'
130
+ // ... implement chat / completion / embedding
131
+ ```
132
+
133
+ Then open a PR โ€” the hook layer stays untouched.
134
+
135
+ ## API reference
136
+
137
+ ### `useChat(options)`
138
+
139
+ Returns a reactive bundle for managing a streaming chat conversation.
140
+
141
+ | Return | Type | Description |
142
+ |---|---|---|
143
+ | `messages` | `Ref<Message[]>` | Full message history (user, assistant, system, tool) |
144
+ | `input` | `Ref<string>` | Bound to your composer; not auto-cleared |
145
+ | `isLoading` | `Ref<boolean>` | True while a stream is in flight |
146
+ | `error` | `Ref<Error \| null>` | Last error, cleared on next `append` |
147
+ | `append(content, opts?)` | `(string \| Message, Partial<ChatRequest>) => Promise<void>` | Send a message and stream the reply |
148
+ | `reload()` | `() => Promise<void>` | Re-run the last assistant turn |
149
+ | `stop()` | `() => void` | Abort the in-flight stream |
150
+ | `setMessages(messages)` | `(Message[]) => void` | Replace history (e.g. on restore) |
151
+ | `clear()` | `() => void` | Reset to empty state |
152
+ | `abortController` | `Ref<AbortController \| null>` | Exposed for advanced use cases |
153
+
154
+ ### `useCompletion(options)` / `useEmbedding(options)`
155
+
156
+ Same shape, scoped to single-shot completions and embedding vectors respectively.
157
+ See [the source](./src/composables/) for full type definitions.
158
+
159
+ ## Examples
160
+
161
+ Three runnable examples live in [`examples/`](./examples):
162
+
163
+ - `examples/chat` โ€” minimal streaming chat UI
164
+ - `examples/completion` โ€” single-shot completion form
165
+ - `examples/embedding` โ€” pairwise cosine similarity heatmap
166
+
167
+ To run them:
168
+
169
+ ```bash
170
+ pnpm install
171
+ echo "VITE_OPENAI_KEY=sk-..." > .env
172
+ pnpm example:chat
173
+ ```
174
+
175
+ ## Project status
176
+
177
+ This is **v0.2.0** โ€” a working foundation, not feature-complete. What's in:
178
+
179
+ - โœ… Chat with streaming, abort, message history
180
+ - โœ… Single-shot completion
181
+ - โœ… Embedding
182
+ - โœ… OpenAI + OpenAI-compatible provider
183
+ - โœ… Anthropic Claude provider
184
+ - โœ… Multimodal image input
185
+ - โœ… localStorage persistence
186
+ - โœ… Tool-calling helpers
187
+ - โœ… Tests, CI, examples
188
+
189
+ What we're planning next:
190
+
191
+ - ๐Ÿ”œ Vue DevTools tab for inspecting streams
192
+ - ๐Ÿ”œ More providers and production hardening
193
+
194
+ ## Contributing
195
+
196
+ Contributions are very welcome. See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for
197
+ the workflow, and the [open issues](../../issues) for things that need hands.
198
+
199
+ Adding a new provider is the single best first contribution โ€” it's one file,
200
+ a small interface, and high-value. See [`src/providers/openai.ts`](./src/providers/openai.ts)
201
+ for the reference implementation.
202
+
203
+ ## License
204
+
205
+ [MIT](./LICENSE)
@@ -0,0 +1,178 @@
1
+ # vue-ai-hooks
2
+
3
+ [English](./README.md) | ็ฎ€ไฝ“ไธญๆ–‡
4
+
5
+ > ็”จไบŽๆž„ๅปบ AI ๅบ”็”จ็š„ Vue 3 ็ป„ๅˆๅผๅ‡ฝๆ•ฐๅบ“ใ€‚
6
+ > ๆตๅผไผ˜ๅ…ˆใ€ๅคš Providerใ€ๅฎŒๆ•ด็ฑปๅž‹ๆ”ฏๆŒใ€‚
7
+
8
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![Vue 3](https://img.shields.io/badge/vue-3.4+-42b883.svg)](https://vuejs.org)
10
+ [![TypeScript](https://img.shields.io/badge/typescript-strict-3178c6.svg)](https://www.typescriptlang.org)
11
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
12
+
13
+ `vue-ai-hooks` ๆŠŠไฝ ๅœจ [VueUse](https://vueuse.org) ๆˆ– [Axios](https://axios-http.com) ไธญ็†Ÿๆ‚‰็š„ๅผ€ๅ‘ไฝ“้ชŒๅธฆๅˆฐ LLM ๅบ”็”จ้‡Œใ€‚ๅฎƒๆไพ›ไธ‰ไธช็ป„ๅˆๅผๅ‡ฝๆ•ฐใ€ๅฏๆ’ๆ‹” Provider๏ผŒๅนถๅธฎไฝ ๅค„็† Server-Sent Events ๆตๅผๅ“ๅบ”ใ€‚ๆ”ฏๆŒ OpenAI ไปฅๅŠไปปไฝ• OpenAI-compatible ๆœๅŠก๏ผŒไพ‹ๅฆ‚ DeepSeekใ€Moonshotใ€ๆ™บ่ฐฑใ€Ollama ็š„ OpenAI shimใ€vLLM ็ญ‰ใ€‚
14
+
15
+ ```ts
16
+ import { useChat, openai } from 'vue-ai-hooks'
17
+
18
+ const { messages, input, append, isLoading, stop } = useChat({
19
+ provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
20
+ })
21
+ ```
22
+
23
+ ## ไธบไป€ไนˆ้œ€่ฆๅฎƒ
24
+
25
+ ๅฝ“ๅ‰ Vue ไธญ็š„ AI ๅผ€ๅ‘ไฝ“้ชŒไป็„ถๆฏ”่พƒๅˆ†ๆ•ฃ๏ผš
26
+
27
+ | ๅบ“ | ๅ–่ˆ |
28
+ |---|---|
29
+ | **Vercel AI SDK** | React ไผ˜ๅ…ˆ๏ผ›Vue ๆ”ฏๆŒ้žๅฎ˜ๆ–นไธ”้€šๅธธๆปžๅŽ |
30
+ | **LangChain.js** | ๅŠŸ่ƒฝๅผบๅคงไฝ†ๅ้‡๏ผ›้“พๅผๆŠฝ่ฑก่พƒๅคš๏ผŒ้ญ”ๆณ•ไนŸๅคš |
31
+ | **็›ดๆŽฅ fetch + ๆ‰‹ๅ†™ SSE** | ๅฏ่กŒ๏ผŒไฝ†ๆฏไธช้กน็›ฎ้ƒฝ่ฆ้‡ๅคๅฎž็Žฐไธญๆญขใ€้‡่ฏ•ๅ’Œ็Šถๆ€็ฎก็† |
32
+ | **vue-ai-hooks** | ไธ“ๆณจใ€ๆก†ๆžถๅŽŸ็”Ÿ๏ผŒๅนถๆŠŠ็น็้ƒจๅˆ†ๅค„็†ๅฅฝ |
33
+
34
+ ## ็‰นๆ€ง
35
+
36
+ - **ไธ‰ไธช็ป„ๅˆๅผๅ‡ฝๆ•ฐ๏ผŒไธ€ๅฅ—ๅฟƒๆ™บๆจกๅž‹**๏ผš`useChat`ใ€`useCompletion`ใ€`useEmbedding`
37
+ - **้ป˜่ฎคๆ”ฏๆŒๆตๅผๅ“ๅบ”**๏ผšSSE ่งฃๆžใ€AbortController ๅ’Œๅ“ๅบ”ๅผ็Šถๆ€้ƒฝๅทฒๅค„็†ๅฅฝ
38
+ - **ๅคš Provider**๏ผšOpenAIใ€Azure OpenAIใ€DeepSeekใ€Moonshotใ€ๆ™บ่ฐฑใ€Ollamaใ€vLLM๏ผŒไปฅๅŠไปปไฝ• OpenAI-compatible API
39
+ - **Tool calling helper**๏ผšๆณจๅ†Œๆœฌๅœฐ handler ๅŽ๏ผŒ`useChat` ไผš่‡ชๅŠจๆ‰ง่กŒๅทฅๅ…ทๅนถ็ปง็ปญๆจกๅž‹่ฝฎๆฌก
40
+ - **TypeScript ไผ˜ๅ…ˆ**๏ผšไธฅๆ ผๆจกๅผใ€ๆ—  `any` ๆณ„ๆผใ€ๅฎŒๆ•ด IDE ่‡ชๅŠจ่กฅๅ…จ
41
+ - **ๅฐ่€Œ่ฝป**๏ผš้™ค Vue ๆœฌ่บซๅค–ๆฒกๆœ‰่ฟ่กŒๆ—ถไพ่ต–
42
+ - **ๅทฒๆต‹่ฏ•**๏ผšVitest + happy-dom๏ผŒๅนถๆไพ›ๅฏๅคๅˆถ็š„ fake provider
43
+ - **ๅฏ tree-shaking**๏ผšESM ๅ’Œ CJSใ€ๅ‘ฝๅๅฏผๅ‡บใ€ๆ— ๅ‰ฏไฝœ็”จ
44
+
45
+ ## ๅฎ‰่ฃ…
46
+
47
+ ```bash
48
+ pnpm add vue-ai-hooks
49
+ # ๆˆ–
50
+ npm install vue-ai-hooks
51
+ # ๆˆ–
52
+ yarn add vue-ai-hooks
53
+ ```
54
+
55
+ Peer dependency๏ผš`vue@^3.4.0`ใ€‚
56
+
57
+ ## ๅฟซ้€Ÿๅผ€ๅง‹
58
+
59
+ ### ๆตๅผ่Šๅคฉ
60
+
61
+ ```vue
62
+ <script setup lang="ts">
63
+ import { useChat, openai } from 'vue-ai-hooks'
64
+
65
+ const { messages, input, append, isLoading, stop, error } = useChat({
66
+ provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
67
+ })
68
+ </script>
69
+
70
+ <template>
71
+ <div v-for="m in messages" :key="m.id" :class="m.role">
72
+ {{ m.content }}
73
+ </div>
74
+ <textarea v-model="input" />
75
+ <button :disabled="isLoading" @click="append(input); input = ''">Send</button>
76
+ <button :disabled="!isLoading" @click="stop">Stop</button>
77
+ </template>
78
+ ```
79
+
80
+ ### ๅ•ๆฌก่กฅๅ…จ
81
+
82
+ ```ts
83
+ import { useCompletion, openai } from 'vue-ai-hooks'
84
+
85
+ const { completion, complete } = useCompletion({
86
+ provider: openai({ apiKey: '...' })
87
+ })
88
+
89
+ await complete('Write a haiku about TypeScript:')
90
+ ```
91
+
92
+ ### Embeddings
93
+
94
+ ```ts
95
+ import { useEmbedding, openai } from 'vue-ai-hooks'
96
+
97
+ const { embed, embeddings } = useEmbedding({
98
+ provider: openai({ apiKey: '...' })
99
+ })
100
+
101
+ const result = await embed(['hello world', 'goodbye world'])
102
+ console.log(result.embeddings) // number[][]
103
+ ```
104
+
105
+ ## ไฝฟ็”จ้ž OpenAI Provider
106
+
107
+ ๆฏไธช Provider ้ƒฝๅฎž็ŽฐๅŒไธ€ไธช `ChatProvider` ๆŽฅๅฃ๏ผŒๆ‰€ไปฅ็ป„ๅˆๅผๅ‡ฝๆ•ฐไธๅ…ณๅฟƒๅฆไธ€็ซฏๅ…ทไฝ“ไฝฟ็”จๅ“ชไธชๆจกๅž‹๏ผš
108
+
109
+ ```ts
110
+ import { useChat, openaiCompatible } from 'vue-ai-hooks'
111
+
112
+ useChat({
113
+ provider: openaiCompatible({
114
+ apiKey: 'sk-...',
115
+ baseURL: 'https://api.deepseek.com/v1'
116
+ })
117
+ })
118
+ ```
119
+
120
+ ๆ–ฐๅขž Provider ๅช้œ€่ฆๅฎž็Žฐ `ChatProvider`๏ผš
121
+
122
+ ```ts
123
+ // src/providers/anthropic.ts
124
+ import type { ChatProvider } from 'vue-ai-hooks'
125
+ // ... implement chat / completion / embedding
126
+ ```
127
+
128
+ ็„ถๅŽๆไบค PR๏ผŒ็ป„ๅˆๅผๅ‡ฝๆ•ฐๅฑ‚ไธ้œ€่ฆๆ”นๅŠจใ€‚
129
+
130
+ ## ๆ–‡ๆกฃ
131
+
132
+ - [English docs](./docs/index.md)
133
+ - [ไธญๆ–‡ๆ–‡ๆกฃ](./docs/zh/index.md)
134
+
135
+ ## ็คบไพ‹
136
+
137
+ ไธ‰ไธชๅฏ่ฟ่กŒ็คบไพ‹ไฝไบŽ [`examples/`](./examples)๏ผš
138
+
139
+ - `examples/chat`๏ผšๆœ€ๅฐๆตๅผ่Šๅคฉ UI
140
+ - `examples/completion`๏ผšๅ•ๆฌก่กฅๅ…จ่กจๅ•
141
+ - `examples/embedding`๏ผšๆˆๅฏนไฝ™ๅผฆ็›ธไผผๅบฆ็ƒญๅŠ›ๅ›พ
142
+
143
+ ่ฟ่กŒๆ–นๅผ๏ผš
144
+
145
+ ```bash
146
+ pnpm install
147
+ echo "VITE_OPENAI_KEY=sk-..." > .env
148
+ pnpm example:chat
149
+ ```
150
+
151
+ ## ้กน็›ฎ็Šถๆ€
152
+
153
+ ่ฟ™ๆ˜ฏ **v0.2.0**๏ผŒๆ˜ฏไธ€ไธชๅฏๅทฅไฝœ็š„ๅŸบ็ก€็‰ˆๆœฌ๏ผŒไฝ†่ฟ˜ไธๆ˜ฏๅŠŸ่ƒฝๅฎŒๅค‡็‰ˆๆœฌใ€‚็›ฎๅ‰ๅทฒๅŒ…ๅซ๏ผš
154
+
155
+ - Chat ๆตๅผๅ“ๅบ”ใ€ไธญๆญขใ€ๆถˆๆฏๅކๅฒ
156
+ - ๅ•ๆฌก่กฅๅ…จ
157
+ - Embedding
158
+ - OpenAI + OpenAI-compatible Provider
159
+ - Anthropic Claude Provider
160
+ - ๅคšๆจกๆ€ๅ›พ็‰‡่พ“ๅ…ฅ
161
+ - localStorage ๆŒไน…ๅŒ–
162
+ - Tool-calling helper
163
+ - ๆต‹่ฏ•ใ€CIใ€็คบไพ‹
164
+
165
+ ไธ‹ไธ€ๆญฅ่ฎกๅˆ’๏ผš
166
+
167
+ - ็”จไบŽๆฃ€ๆŸฅๆต็š„ Vue DevTools tab
168
+ - ๆ›ดๅคš Provider ๅ’Œ็”Ÿไบง็บงๅขžๅผบ
169
+
170
+ ## ่ดก็Œฎ
171
+
172
+ ๆฌข่ฟŽ่ดก็Œฎใ€‚่ฏทๆŸฅ็œ‹ [`CONTRIBUTING.md`](./CONTRIBUTING.md) ไบ†่งฃๅทฅไฝœๆต๏ผŒไนŸๅฏไปฅๆŸฅ็œ‹ [open issues](../../issues) ไธญๅพ…ๅค„็†็š„ไบ‹้กนใ€‚
173
+
174
+ ๆ–ฐๅขž Provider ๆ˜ฏๆœ€้€‚ๅˆไฝœไธบ็ฌฌไธ€ๆฌก่ดก็Œฎ็š„ๆ–นๅ‘๏ผšๅช้œ€่ฆไธ€ไธชๆ–‡ไปถใ€ไธ€ไธชๅฐๆŽฅๅฃ๏ผŒ่€Œไธ”ไปทๅ€ผๅพˆ้ซ˜ใ€‚ๅ‚่€ƒๅฎž็Žฐ่ง [`src/providers/openai.ts`](./src/providers/openai.ts)ใ€‚
175
+
176
+ ## License
177
+
178
+ [MIT](./LICENSE)
@@ -0,0 +1,13 @@
1
+ import type { ToolCall } from '../types';
2
+ export interface ToolCallDelta {
3
+ index?: number;
4
+ id?: string;
5
+ type?: 'function';
6
+ function?: {
7
+ name?: string;
8
+ arguments?: string;
9
+ };
10
+ }
11
+ /** Merge streaming tool_call deltas (OpenAI format) into a stable array. */
12
+ export declare function mergeDeltas(existing: ToolCall[] | undefined, delta: ToolCallDelta[] | undefined): ToolCall[];
13
+ //# sourceMappingURL=_tc_merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_tc_merge.d.ts","sourceRoot":"","sources":["../../src/composables/_tc_merge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACjD;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,QAAQ,EAAE,CAkB5G"}
@@ -0,0 +1,39 @@
1
+ import { type Ref } from 'vue';
2
+ import type { ChatProvider } from '../providers/types';
3
+ import type { ChatRequest, Message, Tool, ToolCall } from '../types';
4
+ export interface ToolCallHandlerContext {
5
+ toolCall: ToolCall;
6
+ messages: Message[];
7
+ }
8
+ export type ToolCallHandler = (args: unknown, context: ToolCallHandlerContext) => unknown | Promise<unknown>;
9
+ export interface UseChatOptions {
10
+ provider?: ChatProvider;
11
+ initialMessages?: Message[];
12
+ defaultRequest?: Partial<ChatRequest>;
13
+ id?: string;
14
+ persist?: {
15
+ key: string;
16
+ version?: number;
17
+ };
18
+ tools?: Tool[];
19
+ toolChoice?: ChatRequest['toolChoice'];
20
+ toolHandlers?: Record<string, ToolCallHandler>;
21
+ maxToolRoundtrips?: number;
22
+ onUpdate?: (m: Message) => void;
23
+ onFinish?: (m: Message) => void;
24
+ onError?: (e: Error) => void;
25
+ }
26
+ export interface UseChatReturn {
27
+ messages: Ref<Message[]>;
28
+ input: Ref<string>;
29
+ isLoading: Ref<boolean>;
30
+ error: Ref<Error | null>;
31
+ append: (c: string | Message, o?: Partial<ChatRequest>) => Promise<void>;
32
+ reload: () => Promise<void>;
33
+ stop: () => void;
34
+ setMessages: (m: Message[]) => void;
35
+ clear: () => void;
36
+ abortController: Ref<AbortController | null>;
37
+ }
38
+ export declare function useChat(options: UseChatOptions): UseChatReturn;
39
+ //# sourceMappingURL=useChat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/composables/useChat.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAe,IAAI,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAIjF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,QAAQ,CAAA;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAA;CACpB;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,sBAAsB,KAC5B,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAE/B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,eAAe,CAAC,EAAE,OAAO,EAAE,CAAA;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACrC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAC/B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;IACxB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACxB,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxE,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IACnC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,eAAe,EAAE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;CAC7C;AAID,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CA+O9D"}
@@ -0,0 +1,31 @@
1
+ import { type Ref } from 'vue';
2
+ import type { ChatProvider } from '../providers/types';
3
+ import type { CompletionRequest } from '../types';
4
+ export interface UseCompletionOptions {
5
+ provider: ChatProvider;
6
+ initialCompletion?: string;
7
+ defaultRequest?: Partial<CompletionRequest>;
8
+ onFinish?: (completion: string) => void;
9
+ onError?: (err: Error) => void;
10
+ }
11
+ export interface UseCompletionReturn {
12
+ completion: Ref<string>;
13
+ input: Ref<string>;
14
+ isLoading: Ref<boolean>;
15
+ error: Ref<Error | null>;
16
+ complete: (prompt?: string, options?: Partial<CompletionRequest>) => Promise<string>;
17
+ stop: () => void;
18
+ setCompletion: (value: string) => void;
19
+ abortController: Ref<AbortController | null>;
20
+ }
21
+ /**
22
+ * Vue 3 composable for single-shot streaming completions.
23
+ *
24
+ * ```ts
25
+ * const { completion, input, complete } = useCompletion({
26
+ * provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
27
+ * })
28
+ * ```
29
+ */
30
+ export declare function useCompletion(options: UseCompletionOptions): UseCompletionReturn;
31
+ //# sourceMappingURL=useCompletion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCompletion.d.ts","sourceRoot":"","sources":["../../src/composables/useCompletion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAEjD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,YAAY,CAAA;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC3C,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACvB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACxB,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACpF,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,eAAe,EAAE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;CAC7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAqEhF"}
@@ -0,0 +1,29 @@
1
+ import { type Ref } from 'vue';
2
+ import type { ChatProvider } from '../providers/types';
3
+ import type { EmbeddingRequest, EmbeddingResult } from '../types';
4
+ export interface UseEmbeddingOptions {
5
+ provider: ChatProvider;
6
+ defaultRequest?: Partial<EmbeddingRequest>;
7
+ onSuccess?: (result: EmbeddingResult) => void;
8
+ onError?: (err: Error) => void;
9
+ }
10
+ export interface UseEmbeddingReturn {
11
+ embeddings: Ref<number[][]>;
12
+ isLoading: Ref<boolean>;
13
+ error: Ref<Error | null>;
14
+ result: Ref<EmbeddingResult | null>;
15
+ embed: (input: string | string[], options?: Partial<EmbeddingRequest>) => Promise<EmbeddingResult>;
16
+ abortController: Ref<AbortController | null>;
17
+ }
18
+ /**
19
+ * Vue 3 composable for generating text embeddings.
20
+ *
21
+ * ```ts
22
+ * const { embed, embeddings, isLoading } = useEmbedding({
23
+ * provider: openai({ apiKey: import.meta.env.VITE_OPENAI_KEY })
24
+ * })
25
+ * const { embeddings: vecs } = await embed('hello world')
26
+ * ```
27
+ */
28
+ export declare function useEmbedding(options: UseEmbeddingOptions): UseEmbeddingReturn;
29
+ //# sourceMappingURL=useEmbedding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEmbedding.d.ts","sourceRoot":"","sources":["../../src/composables/useEmbedding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAEjE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,YAAY,CAAA;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC3B,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACxB,MAAM,EAAE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IACnC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;IAClG,eAAe,EAAE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;CAC7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA6C7E"}
@@ -0,0 +1,48 @@
1
+ import { type Ref } from 'vue';
2
+ /**
3
+ * Options for `usePersist` โ€” drop-in localStorage persistence for a single
4
+ * reactive ref.
5
+ */
6
+ export interface UsePersistOptions<T> {
7
+ /** localStorage key. Required. */
8
+ key: string;
9
+ /**
10
+ * Bump this when the shape of `T` changes incompatibly. The persisted
11
+ * payload is stored under `${key}:v${version}` so old data is naturally
12
+ * ignored. Omit to skip versioning.
13
+ */
14
+ version?: number;
15
+ /**
16
+ * Custom serializer. Receives the current value, returns the JSON-safe
17
+ * representation. Default: identity.
18
+ */
19
+ serialize?: (value: T) => unknown;
20
+ /**
21
+ * Custom deserializer. Receives the parsed JSON, returns the value (or null
22
+ * to discard it). Default: identity.
23
+ */
24
+ deserialize?: (raw: unknown) => T | null;
25
+ /**
26
+ * Override the storage. Default: `window.localStorage` when available.
27
+ * Pass an in-memory shim in tests.
28
+ */
29
+ storage?: Storage | null;
30
+ /**
31
+ * Ref to write errors to. Errors during load are silently skipped (the
32
+ * caller can decide to surface them); errors during save are written here.
33
+ */
34
+ onError?: (err: Error) => void;
35
+ }
36
+ /**
37
+ * Wire a ref up to localStorage. Hydrates on creation, writes on every
38
+ * mutation. Returns a `clear()` function that removes the storage entry.
39
+ *
40
+ * ```ts
41
+ * const messages = ref<Message[]>([])
42
+ * const { clear } = usePersist(messages, { key: 'my-chat' })
43
+ * ```
44
+ */
45
+ export declare function usePersist<T>(source: Ref<T>, options: UsePersistOptions<T>): {
46
+ clear: () => void;
47
+ };
48
+ //# sourceMappingURL=usePersist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePersist.d.ts","sourceRoot":"","sources":["../../src/composables/usePersist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAErC;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAA;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,GAAG,IAAI,CAAA;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAC5E,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAiDA"}
package/dist/index.cjs ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";var Z=Object.defineProperty;var q=(u,m,s)=>m in u?Z(u,m,{enumerable:!0,configurable:!0,writable:!0,value:s}):u[m]=s;var B=(u,m,s)=>q(u,typeof m!="symbol"?m+"":m,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("vue");function F(u,m){var g,i,b,l;const s=u?[...u]:[];for(const a of m??[]){const f=a.index;f!==void 0&&(s[f]?(a.id&&(s[f].id=a.id),(b=a.function)!=null&&b.name&&(s[f].function.name+=a.function.name),(l=a.function)!=null&&l.arguments&&(s[f].function.arguments+=a.function.arguments)):s[f]={id:a.id??"",type:"function",function:{name:((g=a.function)==null?void 0:g.name)??"",arguments:((i=a.function)==null?void 0:i.arguments)??""}})}return s}function X(u,m){const{key:s,version:g,serialize:i,deserialize:b,storage:l,onError:a}=m,f=g!==void 0?`${s}:v${g}`:s,p=l!==void 0?l:typeof window<"u"?window.localStorage:null;if(p)try{const k=p.getItem(f);if(k!==null){const x=JSON.parse(k),t=b?b(x):x;t!=null&&(u.value=t)}}catch{}p&&P.watch(u,k=>{try{const x=i?i(k):k;p.setItem(f,JSON.stringify(x))}catch(x){a==null||a(x instanceof Error?x:new Error(String(x)))}},{deep:!0});function n(){if(p)try{p.removeItem(f)}catch{}}return{clear:n}}let G=0;function W(u="msg"){return G+=1,`${u}_${Date.now().toString(36)}_${G.toString(36)}`}class M extends Error{constructor(s,g={}){super(s);B(this,"cause");B(this,"status");this.name="AiHooksError",this.cause=g.cause,this.status=g.status}}const ee=[];function te(u){const{provider:m,initialMessages:s=ee,defaultRequest:g={},onUpdate:i,onFinish:b,onError:l,persist:a,tools:f,toolChoice:p,toolHandlers:n,maxToolRoundtrips:k=1}=u;if(!m)throw new Error("useChat requires a provider option");const x=m,t=P.ref([...s]),d=P.ref(""),h=P.ref(!1),T=P.ref(null),_=P.shallowRef(null),e=a?X(t,{key:a.key,version:a.version,onError:o=>{T.value=o}}):null;function c(o){t.value=[...o]}function v(){_.value&&_.value.abort(),_.value=null,t.value=[],T.value=null,h.value=!1,d.value="",e==null||e.clear()}function r(){_.value&&_.value.abort(),_.value=null,h.value=!1}function A(){return{id:W("assistant"),role:"assistant",content:"",createdAt:new Date}}function $(o){return o instanceof Error?o:new Error(String(o))}function y(o){const w=$(o);return T.value=w,l==null||l(w),w}function D(o){const w=o.function.arguments.trim();if(!w)return{};try{return JSON.parse(w)}catch(C){throw new M(`Invalid JSON arguments for tool "${o.function.name}"`,{cause:C})}}function L(o){return typeof o=="string"?o:o===void 0?"":JSON.stringify(o)}async function S(o){const w=[];for(const C of o){const R=n==null?void 0:n[C.function.name];if(!R)throw new M(`No tool handler registered for "${C.function.name}"`);const E=await R(D(C),{toolCall:C,messages:[...t.value]});w.push({id:W("tool"),role:"tool",content:L(E),toolCallId:C.id,createdAt:new Date})}return w}async function O(o,w){var R;const C=new AbortController;_.value=C,h.value=!0,T.value=null;try{const E={...g,...f&&!w.tools?{tools:f}:{},...p&&!w.toolChoice?{toolChoice:p}:{},...w,signal:C.signal},I=await x.chat(E);for await(const J of I){if(J.content){o.content+=J.content;const N=t.value.findIndex(U=>U.id===o.id);N>=0&&(t.value=[...t.value.slice(0,N),{...o},...t.value.slice(N+1)]),i==null||i({...o})}if((R=J.toolCalls)!=null&&R.length){o.toolCalls=F(o.toolCalls,J.toolCalls);const N=t.value.findIndex(U=>U.id===o.id);N>=0&&(t.value=[...t.value.slice(0,N),{...o},...t.value.slice(N+1)])}if(J.finishReason){o.metadata={...o.metadata??{},finishReason:J.finishReason};const N=t.value.findIndex(U=>U.id===o.id);N>=0&&(t.value=[...t.value.slice(0,N),{...o},...t.value.slice(N+1)])}}b==null||b({...o})}catch(E){const I=$(E);if(I.name==="AbortError"||C.signal.aborted){b==null||b({...o});return}throw T.value=I,l==null||l(I),I}finally{_.value=null,h.value=!1}}async function j(o,w,C){await O(o,w);const R=o.toolCalls;if(!n||!(R!=null&&R.length))return;if(C<=0)throw y(new M("Maximum tool roundtrips exceeded"));h.value=!0;let E;try{E=await S(R)}catch(J){throw h.value=!1,y(J)}const I=A();t.value=[...t.value,...E,I],await j(I,{messages:t.value.filter(J=>J.id!==I.id)},C-1)}async function z(o,w={}){const C=typeof o=="string"?{id:W("user"),role:"user",content:o,createdAt:new Date}:{...o,id:o.id||W(o.role)},R=A();t.value=[...t.value,C,R],await j(R,{messages:t.value.filter(E=>E.id!==R.id),...w},k)}async function H(){var R;const o=(R=[...t.value].map((E,I)=>({m:E,i:I})).reverse().find(({m:E})=>E.role==="assistant"))==null?void 0:R.i;if(o===void 0)throw new M("reload() called with no assistant message to re-run");const w=t.value.slice(0,o),C=A();t.value=[...w,C],await j(C,{messages:w},k)}return{messages:t,input:d,isLoading:h,error:T,append:z,reload:H,stop:r,setMessages:c,clear:v,abortController:_}}function oe(u){const{provider:m,initialCompletion:s="",defaultRequest:g={},onFinish:i,onError:b}=u,l=P.ref(s),a=P.ref(""),f=P.ref(!1),p=P.ref(null),n=P.shallowRef(null);function k(){n.value&&n.value.abort(),n.value=null,f.value=!1}function x(d){l.value=d}async function t(d,h={}){const T=d??a.value;if(!T)throw new Error("complete() requires a prompt (either as argument or via input.value)");const _=new AbortController;n.value=_,f.value=!0,p.value=null,l.value="";try{const e=await m.completion({...g,...h,prompt:T,signal:_.signal,stream:!0});for await(const c of e)l.value+=c;return i==null||i(l.value),l.value}catch(e){const c=e instanceof Error?e:new Error(String(e));if(c.name==="AbortError"||_.signal.aborted)return i==null||i(l.value),l.value;throw p.value=c,b==null||b(c),c}finally{n.value=null,f.value=!1}}return{completion:l,input:a,isLoading:f,error:p,complete:t,stop:k,setCompletion:x,abortController:n}}function ne(u){const{provider:m,defaultRequest:s={},onSuccess:g,onError:i}=u,b=P.ref([]),l=P.ref(!1),a=P.ref(null),f=P.shallowRef(null),p=P.shallowRef(null);async function n(k,x={}){const t=new AbortController;p.value=t,l.value=!0,a.value=null;try{const d=await m.embedding({...s,...x,input:k,signal:t.signal});return b.value=d.embeddings,f.value=d,g==null||g(d),d}catch(d){const h=d instanceof Error?d:new Error(String(d));throw a.value=h,i==null||i(h),h}finally{p.value=null,l.value=!1}}return{embeddings:b,isLoading:l,error:a,result:f,embed:n,abortController:p}}async function*V(u,m){if(!u.body)throw new Error("Response has no body");const s=u.body.getReader(),g=new TextDecoder("utf-8");let i="";try{for(;;){if(m!=null&&m.aborted){await s.cancel();return}const{done:b,value:l}=await s.read();if(b)break;i+=g.decode(l,{stream:!0}).replace(/\r\n/g,`
2
+ `).replace(/\r/g,`
3
+ `);let a;for(;(a=i.indexOf(`
4
+
5
+ `))!==-1;){const f=i.slice(0,a);i=i.slice(a+2);for(const p of f.split(`
6
+ `)){if(!p.startsWith("data:"))continue;const n=p.slice(5).trim();if(n==="[DONE]")return;if(n)try{yield JSON.parse(n)}catch{}}}}}finally{s.releaseLock()}}async function K(u,m={}){const{timeoutMs:s,signal:g,fetcher:i,...b}=m,l=i??(typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):void 0);if(!l)throw new M("No fetch implementation available");const a=new AbortController,f=()=>a.abort();g&&(g.aborted?a.abort():g.addEventListener("abort",f,{once:!0}));let p;s&&s>0&&(p=setTimeout(()=>a.abort(),s));try{const n=await l(u,{...b,signal:a.signal});if(!n.ok){let k;try{k=await n.json()}catch{k=await n.text().catch(()=>{})}throw new M(`Request failed with status ${n.status} ${n.statusText}`,{status:n.status,cause:k})}return n}catch(n){throw n instanceof M?n:(n==null?void 0:n.name)==="AbortError"?new M("Request aborted",{cause:n}):new M("Network error",{cause:n})}finally{p&&clearTimeout(p),g&&g.removeEventListener("abort",f)}}function Y(u){const{apiKey:m,baseURL:s,headers:g={},defaultModel:i,chatPath:b="/chat/completions",completionPath:l="/completions",embeddingPath:a="/embeddings",fetch:f}=u,p={"Content-Type":"application/json",Authorization:`Bearer ${m}`,...g},n=(x,t)=>{const d=x.replace(/\/+$/,""),h=t.startsWith("/")?t:`/${t}`;return`${d}${h}`};function k(x){return x.map(t=>{var h;const d={role:t.role,content:t.content};return t.name&&(d.name=t.name),t.toolCallId&&(d.tool_call_id=t.toolCallId),(h=t.toolCalls)!=null&&h.length&&(d.tool_calls=t.toolCalls),d})}return{id:"openai-compatible",async chat(x){const{messages:t,model:d=i,temperature:h,maxTokens:T,topP:_,frequencyPenalty:e,presencePenalty:c,stop:v,tools:r,toolChoice:A,user:$,stream:y=!0,signal:D,headers:L}=x,S={model:d,messages:k(t),stream:y};h!==void 0&&(S.temperature=h),T!==void 0&&(S.max_tokens=T),_!==void 0&&(S.top_p=_),e!==void 0&&(S.frequency_penalty=e),c!==void 0&&(S.presence_penalty=c),v!==void 0&&(S.stop=v),r&&(S.tools=r),A&&(S.tool_choice=A),$&&(S.user=$);const O=await K(n(s,b),{method:"POST",headers:{...p,...L},body:JSON.stringify(S),signal:D,fetcher:f});if(!y){const j=await O.json();return async function*(){var z,H,o,w,C;yield{content:((o=(H=(z=j.choices)==null?void 0:z[0])==null?void 0:H.message)==null?void 0:o.content)??"",finishReason:((C=(w=j.choices)==null?void 0:w[0])==null?void 0:C.finish_reason)??"stop",usage:j.usage}}()}return async function*(){var j,z,H;for await(const o of V(O,D)){const w=(j=o.choices)==null?void 0:j[0];if(!w)continue;const C=o.usage;yield{content:(z=w.delta)==null?void 0:z.content,toolCalls:(H=w.delta)==null?void 0:H.tool_calls,finishReason:w.finish_reason??void 0,usage:C}}}()},async completion(x){const{prompt:t,model:d=i,temperature:h,maxTokens:T,topP:_,frequencyPenalty:e,presencePenalty:c,stop:v,stream:r=!0,signal:A,headers:$}=x,y={model:d,prompt:t,stream:r};h!==void 0&&(y.temperature=h),T!==void 0&&(y.max_tokens=T),_!==void 0&&(y.top_p=_),e!==void 0&&(y.frequency_penalty=e),c!==void 0&&(y.presence_penalty=c),v!==void 0&&(y.stop=v);const D=await K(n(s,l),{method:"POST",headers:{...p,...$},body:JSON.stringify(y),signal:A,fetcher:f});if(!r){const L=await D.json();return async function*(){var S,O;yield((O=(S=L.choices)==null?void 0:S[0])==null?void 0:O.text)??""}()}return async function*(){var L;for await(const S of V(D,A)){const O=(L=S.choices)==null?void 0:L[0];O!=null&&O.text&&(yield O.text)}}()},async embedding(x){const{input:t,model:d=i,user:h,signal:T,headers:_}=x,e={input:t};d&&(e.model=d),h&&(e.user=h);const v=await(await K(n(s,a),{method:"POST",headers:{...p,..._},body:JSON.stringify(e),signal:T,fetcher:f})).json();return{embeddings:v.data.map(r=>r.embedding),model:v.model,usage:{promptTokens:v.usage.prompt_tokens,totalTokens:v.usage.total_tokens}}}}}function ae(u){return Y({baseURL:"https://api.openai.com/v1",defaultModel:"gpt-4o-mini",...u})}function Q(u){switch(u){case"end_turn":case"stop_sequence":return"stop";case"max_tokens":return"length";case"tool_use":return"tool_calls";default:return"stop"}}function se(u){const{apiKey:m,baseURL:s="https://api.anthropic.com",defaultModel:g="claude-3-5-sonnet-20241022",maxTokens:i=1024,anthropicVersion:b="2023-06-01",headers:l={},fetch:a}=u,f={"Content-Type":"application/json","x-api-key":m,"anthropic-version":b,...l},p=e=>{const c=s.replace(/\/+$/,""),v=e.startsWith("/")?e:`/${e}`;return`${c}${v}`};function n(e){return typeof e=="string"?e:e.map(c=>k(c))}function k(e){if(e.type==="text")return{type:"text",text:e.text};const c=e.image_url.url;if(c.startsWith("data:")){const v=c.match(/^data:([^;]+);base64,(.*)$/s);if(v)return{type:"image",source:{type:"base64",media_type:v[1],data:v[2]}}}return{type:"image",source:{type:"url",url:c}}}function x(e){const c=[],v=[];for(const r of e)r.role==="system"?typeof r.content=="string"&&r.content&&c.push(r.content):(r.role==="user"||r.role==="assistant")&&v.push({role:r.role,content:n(r.content)});return{system:c.length?c.join(`
7
+
8
+ `):void 0,messages:v}}function t(e){const{system:c,messages:v}=x(e.messages),r={model:e.model??g,messages:v,max_tokens:e.maxTokens??i,stream:e.stream??!0};return c&&(r.system=c),e.temperature!==void 0&&(r.temperature=e.temperature),e.topP!==void 0&&(r.top_p=e.topP),e.stop!==void 0&&(r.stop_sequences=Array.isArray(e.stop)?e.stop:[e.stop]),e.user&&(r.metadata={user_id:e.user}),r}async function d(e){const c=t({...e,stream:e.stream??!0}),v=await K(p("/v1/messages"),{method:"POST",headers:{...f,...e.headers},body:JSON.stringify(c),signal:e.signal,fetcher:a});if(e.stream===!1){const r=await v.json();return async function*(){const A=r.content.find($=>$.type==="text");yield{content:(A==null?void 0:A.text)??"",finishReason:Q(r.stop_reason),usage:r.usage?{promptTokens:r.usage.input_tokens,completionTokens:r.usage.output_tokens,totalTokens:r.usage.input_tokens+r.usage.output_tokens}:void 0}}()}return async function*(){var r;for await(const A of V(v,e.signal)){const $=A.type;if($==="content_block_delta"){const y=A.delta;(y==null?void 0:y.type)==="text_delta"&&typeof y.text=="string"&&(yield{content:y.text})}else if($==="message_delta"){const y=(r=A.delta)==null?void 0:r.stop_reason;y&&(yield{finishReason:Q(y)})}else if($==="error"){const y=A.error;throw new M(`Anthropic API error: ${(y==null?void 0:y.type)??"unknown"}: ${(y==null?void 0:y.message)??"unknown"}`,{cause:A})}}}()}async function h(e){return(async function*(){const c=await this.chat({...e,messages:[{id:"prompt",role:"user",content:e.prompt}]});for await(const v of c)v.content&&(yield v.content)}).call(_)}async function T(){throw new M("Anthropic has no embedding API",{status:501})}const _={id:"anthropic",chat:d,completion:h,embedding:T};return _}exports.AiHooksError=M;exports.anthropic=se;exports.openai=ae;exports.openaiCompatible=Y;exports.useChat=te;exports.useCompletion=oe;exports.useEmbedding=ne;exports.usePersist=X;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * vue-ai-hooks โ€” Vue 3 Composable library for AI applications.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { useChat, type ToolCallHandler, type ToolCallHandlerContext, type UseChatOptions, type UseChatReturn } from './composables/useChat';
7
+ export { useCompletion, type UseCompletionOptions, type UseCompletionReturn } from './composables/useCompletion';
8
+ export { useEmbedding, type UseEmbeddingOptions, type UseEmbeddingReturn } from './composables/useEmbedding';
9
+ export { openai, openaiCompatible, type OpenAiLikeConfig } from './providers/openai';
10
+ export { anthropic, type AnthropicConfig } from './providers/anthropic';
11
+ export { type ChatProvider } from './providers/types';
12
+ export { usePersist, type UsePersistOptions } from './composables/usePersist';
13
+ export type { Message, MessageRole, MessageContent, ContentPart, TextPart, ImageUrlPart, Tool, ToolCall, ChatRequest, ChatChunk, CompletionRequest, EmbeddingRequest, EmbeddingResult } from './types';
14
+ export { AiHooksError } from './types';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACpF,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAG7E,YAAY,EACV,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}