@ai-sdk/angular 3.0.153 → 3.0.155
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/CHANGELOG.md +21 -0
- package/README.md +16 -668
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @ai-sdk/angular
|
|
2
2
|
|
|
3
|
+
## 3.0.155
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [57086ff]
|
|
8
|
+
- Updated dependencies [b6b9dca]
|
|
9
|
+
- Updated dependencies [95348b3]
|
|
10
|
+
- @ai-sdk/provider-utils@5.0.70
|
|
11
|
+
- ai@7.0.155
|
|
12
|
+
|
|
13
|
+
## 3.0.154
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [47236dd]
|
|
18
|
+
- Updated dependencies [c8e8866]
|
|
19
|
+
- Updated dependencies [e64ed82]
|
|
20
|
+
- Updated dependencies [1cf95c6]
|
|
21
|
+
- ai@7.0.154
|
|
22
|
+
- @ai-sdk/provider-utils@5.0.69
|
|
23
|
+
|
|
3
24
|
## 3.0.153
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,688 +1,36 @@
|
|
|
1
|
-
# AI SDK Angular
|
|
1
|
+
# AI SDK - Angular integration
|
|
2
2
|
|
|
3
|
-
Angular UI components for the
|
|
3
|
+
Angular UI components for the AI SDK, built with Angular signals. The package includes `Chat`, `Completion`, and `StructuredObject` for streaming chat, text generation, and structured data.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
The `@ai-sdk/angular` package provides Angular-specific implementations using Angular signals for reactive state management:
|
|
8
|
-
|
|
9
|
-
- **Chat** - Multi-turn conversations with streaming responses
|
|
10
|
-
- **Completion** - Single-turn text generation
|
|
11
|
-
- **StructuredObject** - Type-safe object generation with Zod schemas
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
5
|
+
## Setup
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
|
-
npm
|
|
8
|
+
npm i ai @ai-sdk/angular
|
|
17
9
|
```
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- Angular 16+ (`@angular/core`)
|
|
22
|
-
- Zod v3+ (optional, for structured objects)
|
|
23
|
-
|
|
24
|
-
## Chat
|
|
25
|
-
|
|
26
|
-
Real-time conversation interface with streaming support.
|
|
27
|
-
|
|
28
|
-
### Basic Usage
|
|
11
|
+
## Usage
|
|
29
12
|
|
|
30
|
-
```
|
|
31
|
-
import { Component, inject } from '@angular/core';
|
|
32
|
-
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
33
|
-
import { Chat } from '@ai-sdk/angular';
|
|
13
|
+
```ts
|
|
34
14
|
import { CommonModule } from '@angular/common';
|
|
15
|
+
import { Component } from '@angular/core';
|
|
16
|
+
import { Chat } from '@ai-sdk/angular';
|
|
35
17
|
|
|
36
18
|
@Component({
|
|
37
19
|
selector: 'app-chat',
|
|
38
|
-
|
|
20
|
+
standalone: true,
|
|
21
|
+
imports: [CommonModule],
|
|
39
22
|
template: `
|
|
40
|
-
<div
|
|
41
|
-
<div
|
|
42
|
-
|
|
43
|
-
<div class="message" [ngClass]="message.role">
|
|
44
|
-
@for (part of message.parts; track $index) {
|
|
45
|
-
@switch (part.type) {
|
|
46
|
-
@case ('text') {
|
|
47
|
-
<div style="white-space: pre-wrap">
|
|
48
|
-
{{ part.text }}
|
|
49
|
-
@if (part.state === 'streaming') {
|
|
50
|
-
<span class="cursor">▮</span>
|
|
51
|
-
}
|
|
52
|
-
</div>
|
|
53
|
-
}
|
|
54
|
-
@case ('reasoning') {
|
|
55
|
-
<details>
|
|
56
|
-
<summary>Reasoning</summary>
|
|
57
|
-
<div style="white-space: pre-wrap; opacity: 80%">
|
|
58
|
-
{{ part.text }}
|
|
59
|
-
</div>
|
|
60
|
-
</details>
|
|
61
|
-
}
|
|
62
|
-
@default {
|
|
63
|
-
<code>{{ part | json }}</code>
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
</div>
|
|
68
|
-
}
|
|
69
|
-
@if (chat.status === 'submitted') {
|
|
70
|
-
<div><em>Waiting...</em></div>
|
|
71
|
-
}
|
|
23
|
+
<div *ngFor="let message of chat.messages">
|
|
24
|
+
<div *ngFor="let part of message.parts">
|
|
25
|
+
<p *ngIf="part.type === 'text'">{{ part.text }}</p>
|
|
72
26
|
</div>
|
|
73
|
-
|
|
74
|
-
<form [formGroup]="chatForm" (ngSubmit)="sendMessage()">
|
|
75
|
-
<input formControlName="userInput" placeholder="Type your message..." />
|
|
76
|
-
@if (chat.status === 'ready') {
|
|
77
|
-
<button type="submit" [disabled]="!chatForm.valid">Send</button>
|
|
78
|
-
} @else {
|
|
79
|
-
<button [disabled]="chat.status === 'error'" (click)="chat.stop()">
|
|
80
|
-
Stop
|
|
81
|
-
</button>
|
|
82
|
-
}
|
|
83
|
-
</form>
|
|
84
27
|
</div>
|
|
28
|
+
<button (click)="chat.sendMessage({ text: 'Hello!' })">Send</button>
|
|
85
29
|
`,
|
|
86
30
|
})
|
|
87
31
|
export class ChatComponent {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
public chat = new Chat({});
|
|
91
|
-
|
|
92
|
-
chatForm = this.fb.group({
|
|
93
|
-
userInput: ['', Validators.required],
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
sendMessage() {
|
|
97
|
-
if (this.chatForm.invalid) return;
|
|
98
|
-
|
|
99
|
-
const userInput = this.chatForm.value.userInput;
|
|
100
|
-
this.chatForm.reset();
|
|
101
|
-
|
|
102
|
-
this.chat.sendMessage(
|
|
103
|
-
{ text: userInput },
|
|
104
|
-
{
|
|
105
|
-
body: {
|
|
106
|
-
selectedModel: 'openai/gpt-6-astra',
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
);
|
|
110
|
-
}
|
|
32
|
+
readonly chat = new Chat({});
|
|
111
33
|
}
|
|
112
34
|
```
|
|
113
35
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
### Constructor Options
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
interface ChatInit<UI_MESSAGE extends UIMessage = UIMessage> {
|
|
120
|
-
/** A unique identifier for the chat */
|
|
121
|
-
id?: string;
|
|
122
|
-
|
|
123
|
-
/** Optional metadata schema for UI messages */
|
|
124
|
-
messageMetadataSchema?:
|
|
125
|
-
| Validator<InferUIMessageMetadata<UI_MESSAGE>>
|
|
126
|
-
| StandardSchemaV1<InferUIMessageMetadata<UI_MESSAGE>>;
|
|
127
|
-
|
|
128
|
-
/** Optional data part schemas for UI messages */
|
|
129
|
-
dataPartSchemas?: UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>;
|
|
130
|
-
|
|
131
|
-
/** Initial messages */
|
|
132
|
-
messages?: UI_MESSAGE[];
|
|
133
|
-
|
|
134
|
-
/** Custom ID generator */
|
|
135
|
-
generateId?: IdGenerator;
|
|
136
|
-
|
|
137
|
-
/** Custom transport */
|
|
138
|
-
transport?: ChatTransport<UI_MESSAGE>;
|
|
139
|
-
|
|
140
|
-
/** Maximum conversation steps */
|
|
141
|
-
maxSteps?: number;
|
|
142
|
-
|
|
143
|
-
/** Tool call handler */
|
|
144
|
-
onToolCall?: (params: {
|
|
145
|
-
toolCall: ToolCall<string, unknown>;
|
|
146
|
-
}) => void | Promise<unknown> | unknown;
|
|
147
|
-
|
|
148
|
-
/** Completion callback */
|
|
149
|
-
onFinish?: (params: { message: UI_MESSAGE }) => void;
|
|
150
|
-
|
|
151
|
-
/** Data part callback */
|
|
152
|
-
onData?: (dataPart: DataUIPart<InferUIMessageData<UI_MESSAGE>>) => void;
|
|
153
|
-
|
|
154
|
-
/** Error handler */
|
|
155
|
-
onError?: (error: Error) => void;
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Properties (Reactive)
|
|
160
|
-
|
|
161
|
-
These are values backed by Angular signals and update reactively.
|
|
162
|
-
|
|
163
|
-
- `messages: UIMessage[]` - Array of conversation messages
|
|
164
|
-
- `status: ChatStatus` - Current status
|
|
165
|
-
- `error: Error | undefined` - Current error state
|
|
166
|
-
|
|
167
|
-
### Methods
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// Send a message
|
|
171
|
-
await chat.sendMessage(
|
|
172
|
-
message: UIMessageInput,
|
|
173
|
-
options?: {
|
|
174
|
-
body?: object;
|
|
175
|
-
headers?: Record<string, string> | Headers;
|
|
176
|
-
}
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Regenerate last assistant message
|
|
180
|
-
await chat.regenerate(options?: {
|
|
181
|
-
body?: object;
|
|
182
|
-
headers?: Record<string, string> | Headers;
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// Resume an interrupted stream
|
|
186
|
-
await chat.resumeStream(options?: {
|
|
187
|
-
body?: object;
|
|
188
|
-
headers?: Record<string, string> | Headers;
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Add tool execution result
|
|
192
|
-
chat.addToolResult({
|
|
193
|
-
toolCallId: string;
|
|
194
|
-
output: unknown;
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Stop current generation
|
|
198
|
-
chat.stop();
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### File Attachments
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
// HTML template
|
|
205
|
-
<input type="file" multiple (change)="onFileSelect($event)" />
|
|
206
|
-
|
|
207
|
-
// Component
|
|
208
|
-
onFileSelect(event: Event) {
|
|
209
|
-
const files = (event.target as HTMLInputElement).files;
|
|
210
|
-
if (files) {
|
|
211
|
-
this.chat.sendMessage({
|
|
212
|
-
text: "Analyze these files",
|
|
213
|
-
files: files
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Client-side Tool Calls
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
const chat = new Chat({
|
|
223
|
-
async onToolCall({ toolCall }) {
|
|
224
|
-
switch (toolCall.toolName) {
|
|
225
|
-
case 'get_weather':
|
|
226
|
-
return await getWeather(toolCall.input.location);
|
|
227
|
-
case 'search':
|
|
228
|
-
return await search(toolCall.input.query);
|
|
229
|
-
default:
|
|
230
|
-
throw new Error(`Unknown tool: ${toolCall.toolName}`);
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Completion
|
|
237
|
-
|
|
238
|
-
Single-turn text generation with streaming.
|
|
239
|
-
|
|
240
|
-
### Basic Usage
|
|
241
|
-
|
|
242
|
-
```typescript
|
|
243
|
-
import { Component } from '@angular/core';
|
|
244
|
-
import { Completion } from '@ai-sdk/angular';
|
|
245
|
-
|
|
246
|
-
@Component({
|
|
247
|
-
selector: 'app-completion',
|
|
248
|
-
template: `
|
|
249
|
-
<div>
|
|
250
|
-
<textarea
|
|
251
|
-
[(ngModel)]="completion.input"
|
|
252
|
-
placeholder="Enter your prompt..."
|
|
253
|
-
rows="4"
|
|
254
|
-
>
|
|
255
|
-
</textarea>
|
|
256
|
-
|
|
257
|
-
<button
|
|
258
|
-
(click)="completion.complete(completion.input)"
|
|
259
|
-
[disabled]="completion.loading"
|
|
260
|
-
>
|
|
261
|
-
{{ completion.loading ? 'Generating...' : 'Generate' }}
|
|
262
|
-
</button>
|
|
263
|
-
|
|
264
|
-
@if (completion.loading) {
|
|
265
|
-
<button (click)="completion.stop()">Stop</button>
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
<div class="result">
|
|
269
|
-
<h3>Result:</h3>
|
|
270
|
-
<pre>{{ completion.completion }}</pre>
|
|
271
|
-
</div>
|
|
272
|
-
|
|
273
|
-
@if (completion.error) {
|
|
274
|
-
<div class="error">{{ completion.error.message }}</div>
|
|
275
|
-
}
|
|
276
|
-
</div>
|
|
277
|
-
`,
|
|
278
|
-
})
|
|
279
|
-
export class CompletionComponent {
|
|
280
|
-
completion = new Completion({
|
|
281
|
-
api: '/api/completion',
|
|
282
|
-
streamProtocol: 'text',
|
|
283
|
-
onFinish: (prompt, completion) => {
|
|
284
|
-
console.log('Completed:', { prompt, completion });
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Constructor Options
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
interface CompletionOptions {
|
|
294
|
-
/** API endpoint (default: '/api/completion') */
|
|
295
|
-
api?: string;
|
|
296
|
-
|
|
297
|
-
/** Unique identifier */
|
|
298
|
-
id?: string;
|
|
299
|
-
|
|
300
|
-
/** Initial completion text */
|
|
301
|
-
initialCompletion?: string;
|
|
302
|
-
|
|
303
|
-
/** Initial input text */
|
|
304
|
-
initialInput?: string;
|
|
305
|
-
|
|
306
|
-
/** Stream protocol: 'data' (default) | 'text' */
|
|
307
|
-
streamProtocol?: 'data' | 'text';
|
|
308
|
-
|
|
309
|
-
/** Completion callback */
|
|
310
|
-
onFinish?: (prompt: string, completion: string) => void;
|
|
311
|
-
|
|
312
|
-
/** Error handler */
|
|
313
|
-
onError?: (error: Error) => void;
|
|
314
|
-
|
|
315
|
-
/** Custom fetch function */
|
|
316
|
-
fetch?: FetchFunction;
|
|
317
|
-
|
|
318
|
-
/** Request headers */
|
|
319
|
-
headers?: Record<string, string> | Headers;
|
|
320
|
-
|
|
321
|
-
/** Request body */
|
|
322
|
-
body?: object;
|
|
323
|
-
|
|
324
|
-
/** Request credentials */
|
|
325
|
-
credentials?: RequestCredentials;
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Properties (Reactive)
|
|
330
|
-
|
|
331
|
-
- `completion: string` - Generated text (writable)
|
|
332
|
-
- `input: string` - Current input (writable)
|
|
333
|
-
- `loading: boolean` - Generation state
|
|
334
|
-
- `error: Error | undefined` - Error state
|
|
335
|
-
- `id: string` - Completion ID
|
|
336
|
-
- `api: string` - API endpoint
|
|
337
|
-
- `streamProtocol: 'data' | 'text'` - Stream type
|
|
338
|
-
|
|
339
|
-
### Methods
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
// Generate completion
|
|
343
|
-
await completion.complete(
|
|
344
|
-
prompt: string,
|
|
345
|
-
options?: {
|
|
346
|
-
headers?: Record<string, string> | Headers;
|
|
347
|
-
body?: object;
|
|
348
|
-
}
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// Form submission handler
|
|
352
|
-
await completion.handleSubmit(event?: { preventDefault?: () => void });
|
|
353
|
-
|
|
354
|
-
// Stop generation
|
|
355
|
-
completion.stop();
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## StructuredObject
|
|
359
|
-
|
|
360
|
-
Generate structured data with Zod schemas and streaming.
|
|
361
|
-
|
|
362
|
-
### Basic Usage
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
import { Component } from '@angular/core';
|
|
366
|
-
import { StructuredObject } from '@ai-sdk/angular';
|
|
367
|
-
import { z } from 'zod';
|
|
368
|
-
|
|
369
|
-
const schema = z.object({
|
|
370
|
-
title: z.string(),
|
|
371
|
-
summary: z.string(),
|
|
372
|
-
tags: z.array(z.string()),
|
|
373
|
-
sentiment: z.enum(['positive', 'negative', 'neutral']),
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
@Component({
|
|
377
|
-
selector: 'app-structured-object',
|
|
378
|
-
template: `
|
|
379
|
-
<div>
|
|
380
|
-
<textarea
|
|
381
|
-
[(ngModel)]="input"
|
|
382
|
-
placeholder="Enter content to analyze..."
|
|
383
|
-
rows="4"
|
|
384
|
-
>
|
|
385
|
-
</textarea>
|
|
386
|
-
|
|
387
|
-
<button (click)="analyze()" [disabled]="structuredObject.loading">
|
|
388
|
-
{{ structuredObject.loading ? 'Analyzing...' : 'Analyze' }}
|
|
389
|
-
</button>
|
|
390
|
-
|
|
391
|
-
@if (structuredObject.object) {
|
|
392
|
-
<div class="result">
|
|
393
|
-
<h3>Analysis:</h3>
|
|
394
|
-
<div><strong>Title:</strong> {{ structuredObject.object.title }}</div>
|
|
395
|
-
<div>
|
|
396
|
-
<strong>Summary:</strong> {{ structuredObject.object.summary }}
|
|
397
|
-
</div>
|
|
398
|
-
<div>
|
|
399
|
-
<strong>Tags:</strong>
|
|
400
|
-
{{ structuredObject.object.tags?.join(', ') }}
|
|
401
|
-
</div>
|
|
402
|
-
<div>
|
|
403
|
-
<strong>Sentiment:</strong> {{ structuredObject.object.sentiment }}
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
@if (structuredObject.error) {
|
|
409
|
-
<div class="error">{{ structuredObject.error.message }}</div>
|
|
410
|
-
}
|
|
411
|
-
</div>
|
|
412
|
-
`,
|
|
413
|
-
})
|
|
414
|
-
export class StructuredObjectComponent {
|
|
415
|
-
input = '';
|
|
416
|
-
|
|
417
|
-
structuredObject = new StructuredObject({
|
|
418
|
-
api: '/api/analyze',
|
|
419
|
-
schema,
|
|
420
|
-
onFinish: ({ object, error }) => {
|
|
421
|
-
if (error) {
|
|
422
|
-
console.error('Schema validation failed:', error);
|
|
423
|
-
} else {
|
|
424
|
-
console.log('Generated object:', object);
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
async analyze() {
|
|
430
|
-
if (!this.input.trim()) return;
|
|
431
|
-
await this.structuredObject.submit(this.input);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
### Constructor Options
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
interface StructuredObjectOptions<SCHEMA, RESULT> {
|
|
440
|
-
/** API endpoint */
|
|
441
|
-
api: string;
|
|
442
|
-
|
|
443
|
-
/** Zod schema */
|
|
444
|
-
schema: SCHEMA;
|
|
445
|
-
|
|
446
|
-
/** Unique identifier */
|
|
447
|
-
id?: string;
|
|
448
|
-
|
|
449
|
-
/** Initial object value */
|
|
450
|
-
initialValue?: DeepPartial<RESULT>;
|
|
451
|
-
|
|
452
|
-
/** Completion callback */
|
|
453
|
-
onFinish?: (event: {
|
|
454
|
-
object: RESULT | undefined;
|
|
455
|
-
error: Error | undefined;
|
|
456
|
-
}) => void;
|
|
457
|
-
|
|
458
|
-
/** Error handler */
|
|
459
|
-
onError?: (error: Error) => void;
|
|
460
|
-
|
|
461
|
-
/** Custom fetch function */
|
|
462
|
-
fetch?: FetchFunction;
|
|
463
|
-
|
|
464
|
-
/** Request headers */
|
|
465
|
-
headers?: Record<string, string> | Headers;
|
|
466
|
-
|
|
467
|
-
/** Request credentials */
|
|
468
|
-
credentials?: RequestCredentials;
|
|
469
|
-
}
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
### Properties (Reactive)
|
|
473
|
-
|
|
474
|
-
- `object: DeepPartial<RESULT> | undefined` - Generated object
|
|
475
|
-
- `loading: boolean` - Generation state
|
|
476
|
-
- `error: Error | undefined` - Error state
|
|
477
|
-
|
|
478
|
-
### Methods
|
|
479
|
-
|
|
480
|
-
```typescript
|
|
481
|
-
// Submit input for generation
|
|
482
|
-
await structuredObject.submit(input: unknown);
|
|
483
|
-
|
|
484
|
-
// Stop generation
|
|
485
|
-
structuredObject.stop();
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## Server Implementation
|
|
489
|
-
|
|
490
|
-
When you pass a string model ID (for example `openai/gpt-6-astra`), the AI SDK uses
|
|
491
|
-
AI Gateway as the default provider, so no provider import is required.
|
|
492
|
-
|
|
493
|
-
### Express.js Chat Endpoint
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
import {
|
|
497
|
-
convertToModelMessages,
|
|
498
|
-
pipeTextStreamToResponse,
|
|
499
|
-
pipeUIMessageStreamToResponse,
|
|
500
|
-
streamText,
|
|
501
|
-
toTextStream,
|
|
502
|
-
toUIMessageStream,
|
|
503
|
-
} from 'ai';
|
|
504
|
-
import express from 'express';
|
|
505
|
-
|
|
506
|
-
const app = express();
|
|
507
|
-
app.use(express.json({ strict: false }));
|
|
508
|
-
|
|
509
|
-
app.post('/api/chat', async (req, res) => {
|
|
510
|
-
const { messages, selectedModel } = req.body;
|
|
511
|
-
|
|
512
|
-
const result = streamText({
|
|
513
|
-
model: selectedModel || 'openai/gpt-6-astra',
|
|
514
|
-
messages: convertToModelMessages(messages),
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
pipeUIMessageStreamToResponse({
|
|
518
|
-
response: res,
|
|
519
|
-
stream: toUIMessageStream({ stream: result.stream }),
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Express.js Completion Endpoint
|
|
525
|
-
|
|
526
|
-
```typescript
|
|
527
|
-
app.post('/api/completion', async (req, res) => {
|
|
528
|
-
const { prompt } = req.body;
|
|
529
|
-
|
|
530
|
-
const result = streamText({
|
|
531
|
-
model: 'openai/gpt-6-astra',
|
|
532
|
-
prompt,
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
pipeTextStreamToResponse({
|
|
536
|
-
response: res,
|
|
537
|
-
stream: toTextStream({ stream: result.stream }),
|
|
538
|
-
});
|
|
539
|
-
});
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### Express.js Structured Object Endpoint
|
|
543
|
-
|
|
544
|
-
```typescript
|
|
545
|
-
import { streamObject } from 'ai';
|
|
546
|
-
import { z } from 'zod';
|
|
547
|
-
|
|
548
|
-
app.post('/api/analyze', async (req, res) => {
|
|
549
|
-
const input = req.body;
|
|
550
|
-
|
|
551
|
-
const result = streamObject({
|
|
552
|
-
model: 'openai/gpt-6-astra',
|
|
553
|
-
schema: z.object({
|
|
554
|
-
title: z.string(),
|
|
555
|
-
summary: z.string(),
|
|
556
|
-
tags: z.array(z.string()),
|
|
557
|
-
sentiment: z.enum(['positive', 'negative', 'neutral']),
|
|
558
|
-
}),
|
|
559
|
-
prompt: `Analyze this content: ${JSON.stringify(input)}`,
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
result.pipeTextStreamToResponse(res);
|
|
563
|
-
});
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
## Development Setup
|
|
567
|
-
|
|
568
|
-
### Building the Library
|
|
569
|
-
|
|
570
|
-
```bash
|
|
571
|
-
# Install dependencies
|
|
572
|
-
pnpm install
|
|
573
|
-
|
|
574
|
-
# Build library
|
|
575
|
-
pnpm build
|
|
576
|
-
|
|
577
|
-
# Watch mode
|
|
578
|
-
pnpm build:watch
|
|
579
|
-
|
|
580
|
-
# Run tests
|
|
581
|
-
pnpm test
|
|
582
|
-
|
|
583
|
-
# Test watch mode
|
|
584
|
-
pnpm test:watch
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Running the Example
|
|
588
|
-
|
|
589
|
-
```bash
|
|
590
|
-
# Navigate to example
|
|
591
|
-
cd examples/angular
|
|
592
|
-
|
|
593
|
-
# Set up environment
|
|
594
|
-
echo "AI_GATEWAY_API_KEY=your_key_here" > .env
|
|
595
|
-
|
|
596
|
-
# Alternatively, use OIDC authentication
|
|
597
|
-
# echo "VERCEL_OIDC_TOKEN=your_token_here" > .env
|
|
598
|
-
|
|
599
|
-
# Start development (Angular + Express)
|
|
600
|
-
pnpm start
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
Starts:
|
|
604
|
-
|
|
605
|
-
- Angular dev server: `http://localhost:4200`
|
|
606
|
-
- Express API server: `http://localhost:3000`
|
|
607
|
-
- Proxy routes `/api/*` to Express
|
|
608
|
-
|
|
609
|
-
## Testing
|
|
610
|
-
|
|
611
|
-
### Running Tests
|
|
612
|
-
|
|
613
|
-
```bash
|
|
614
|
-
pnpm test # Run all tests
|
|
615
|
-
pnpm test:watch # Watch mode
|
|
616
|
-
pnpm test:update # Update snapshots
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
## TypeScript Support
|
|
620
|
-
|
|
621
|
-
Full type safety with automatic type inference:
|
|
622
|
-
|
|
623
|
-
```typescript
|
|
624
|
-
import { Chat, UIMessage, StructuredObject } from '@ai-sdk/angular';
|
|
625
|
-
import { z } from 'zod';
|
|
626
|
-
|
|
627
|
-
// Custom message types
|
|
628
|
-
interface CustomMessage extends UIMessage {
|
|
629
|
-
customData?: string;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
const chat = new Chat<CustomMessage>({});
|
|
633
|
-
|
|
634
|
-
// Schema-typed objects
|
|
635
|
-
const schema = z.object({
|
|
636
|
-
name: z.string(),
|
|
637
|
-
age: z.number(),
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
const obj = new StructuredObject({
|
|
641
|
-
api: '/api/object',
|
|
642
|
-
schema, // Type automatically inferred
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
// obj.object has type: { name?: string; age?: number } | undefined
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
## Error Handling
|
|
649
|
-
|
|
650
|
-
All components provide reactive error states:
|
|
651
|
-
|
|
652
|
-
```typescript
|
|
653
|
-
const chat = new Chat({
|
|
654
|
-
onError: (error) => {
|
|
655
|
-
console.error('Chat error:', error);
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
// Template
|
|
660
|
-
@if (chat.error) {
|
|
661
|
-
<div class="error">{{ chat.error.message }}</div>
|
|
662
|
-
}
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
## Performance
|
|
666
|
-
|
|
667
|
-
### Stop on-going requests
|
|
668
|
-
|
|
669
|
-
```typescript
|
|
670
|
-
chat.stop();
|
|
671
|
-
completion.stop();
|
|
672
|
-
structuredObject.stop();
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
### Change Detection
|
|
676
|
-
|
|
677
|
-
Uses Angular signals for efficient reactivity:
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
// These trigger minimal change detection
|
|
681
|
-
chat.messages; // UIMessage[]
|
|
682
|
-
chat.status; // ChatStatus
|
|
683
|
-
chat.error; // Error | undefined
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
## License
|
|
687
|
-
|
|
688
|
-
Apache-2.0
|
|
36
|
+
See the [AI SDK Angular documentation](https://ai-sdk.dev/docs/getting-started/angular) for more information.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/angular",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.155",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Angular implementation of ai-sdk.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
29
|
-
"ai": "7.0.
|
|
28
|
+
"@ai-sdk/provider-utils": "5.0.70",
|
|
29
|
+
"ai": "7.0.155"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@ai-sdk/test-server": "2.0.4",
|