@ai-sdk/angular 3.0.154 → 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.
Files changed (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +16 -668
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
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
+
3
13
  ## 3.0.154
4
14
 
5
15
  ### 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 [AI SDK v6](https://ai-sdk.dev/docs).
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
- ## Overview
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 install @ai-sdk/angular ai
8
+ npm i ai @ai-sdk/angular
17
9
  ```
18
10
 
19
- ### Peer Dependencies
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
- ```typescript
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
- imports: [CommonModule, ReactiveFormsModule],
20
+ standalone: true,
21
+ imports: [CommonModule],
39
22
  template: `
40
- <div class="chat-container">
41
- <div class="messages">
42
- @for (message of chat.messages; track message.id) {
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">&#9646;</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
- private fb = inject(FormBuilder);
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
- `selectedModel` should be an AI Gateway model ID like `openai/gpt-6-astra`.
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.154",
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.69",
29
- "ai": "7.0.154"
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",