Angular implementation of ai-sdk.
npm install @ai-sdk/angularAngular UI components for the AI SDK v5.
The @ai-sdk/angular package provides Angular-specific implementations using Angular signals for reactive state management:
- Chat - Multi-turn conversations with streaming responses
- Completion - Single-turn text generation
- StructuredObject - Type-safe object generation with Zod schemas
``bash`
npm install @ai-sdk/angular ai
- Angular 16+ (@angular/core)
- Zod v3+ (optional, for structured objects)
Real-time conversation interface with streaming support.
`typescript
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { Chat } from '@ai-sdk/angular';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-chat',
imports: [CommonModule, ReactiveFormsModule],
template:
,
})
export class ChatComponent {
private fb = inject(FormBuilder); public chat = new Chat({});
chatForm = this.fb.group({
userInput: ['', Validators.required],
});
sendMessage() {
if (this.chatForm.invalid) return;
const userInput = this.chatForm.value.userInput;
this.chatForm.reset();
this.chat.sendMessage(
{ text: userInput },
{
body: {
selectedModel: 'gpt-4o',
},
},
);
}
}
`$3
`typescript
interface ChatInit {
/* Initial messages /
messages?: UI_MESSAGE[]; /* Custom ID generator /
generateId?: () => string;
/* Maximum conversation steps /
maxSteps?: number;
/* Tool call handler /
onToolCall?: (params: { toolCall: ToolCall }) => Promise;
/* Completion callback /
onFinish?: (params: { message: UI_MESSAGE }) => void;
/* Error handler /
onError?: (error: Error) => void;
/* Custom transport /
transport?: ChatTransport;
}
`$3
-
messages: UIMessage[] - Array of conversation messages
- status: 'ready' | 'submitted' | 'streaming' | 'error' - Current status
- error: Error | undefined - Current error state$3
`typescript
// Send a message
await chat.sendMessage(
message: UIMessageInput,
options?: {
body?: Record;
headers?: Record;
}
);// Regenerate last assistant message
await chat.regenerate(options?: {
body?: Record;
headers?: Record;
});
// Add tool execution result
chat.addToolOutput({
toolCallId: string;
output: string;
});
// Stop current generation
chat.stop();
`$3
`typescript
// HTML template
// Component
onFileSelect(event: Event) {
const files = (event.target as HTMLInputElement).files;
if (files) {
this.chat.sendMessage({
text: "Analyze these files",
files: files
});
}
}
`$3
`typescript
const chat = new Chat({
async onToolCall({ toolCall }) {
switch (toolCall.toolName) {
case 'get_weather':
return await getWeather(toolCall.input.location);
case 'search':
return await search(toolCall.input.query);
default:
throw new Error(Unknown tool: ${toolCall.toolName});
}
},
});
`Completion
Single-turn text generation with streaming.
$3
`typescript
import { Component } from '@angular/core';
import { Completion } from '@ai-sdk/angular';@Component({
selector: 'app-completion',
template:
(click)="completion.complete(completion.input)"
[disabled]="completion.loading"
>
{{ completion.loading ? 'Generating...' : 'Generate' }}
@if (completion.loading) {
}
{{ completion.completion }} @if (completion.error) {
,
})
export class CompletionComponent {
completion = new Completion({
api: '/api/completion',
onFinish: (prompt, completion) => {
console.log('Completed:', { prompt, completion });
},
});
}
`$3
`typescript
interface CompletionOptions {
/* API endpoint (default: '/api/completion') /
api?: string; /* Unique identifier /
id?: string;
/* Initial completion text /
initialCompletion?: string;
/* Initial input text /
initialInput?: string;
/* Stream protocol: 'data' (default) | 'text' /
streamProtocol?: 'data' | 'text';
/* Completion callback /
onFinish?: (prompt: string, completion: string) => void;
/* Error handler /
onError?: (error: Error) => void;
/* Custom fetch function /
fetch?: FetchFunction;
/* Request headers /
headers?: Record;
/* Request body /
body?: Record;
/* Request credentials /
credentials?: RequestCredentials;
}
`$3
-
completion: string - Generated text (writable)
- input: string - Current input (writable)
- loading: boolean - Generation state
- error: Error | undefined - Error state
- id: string - Completion ID
- api: string - API endpoint
- streamProtocol: 'data' | 'text' - Stream type$3
`typescript
// Generate completion
await completion.complete(
prompt: string,
options?: {
headers?: Record;
body?: Record;
}
);// Form submission handler
await completion.handleSubmit(event?: { preventDefault?: () => void });
// Stop generation
completion.stop();
`StructuredObject
Generate structured data with Zod schemas and streaming.
$3
`typescript
import { Component } from '@angular/core';
import { StructuredObject } from '@ai-sdk/angular';
import { z } from 'zod';const schema = z.object({
title: z.string(),
summary: z.string(),
tags: z.array(z.string()),
sentiment: z.enum(['positive', 'negative', 'neutral']),
});
@Component({
selector: 'app-structured-object',
template:
@if (structuredObject.object) {
@if (structuredObject.error) {
,
})
export class StructuredObjectComponent {
input = ''; structuredObject = new StructuredObject({
api: '/api/analyze',
schema,
onFinish: ({ object, error }) => {
if (error) {
console.error('Schema validation failed:', error);
} else {
console.log('Generated object:', object);
}
},
});
async analyze() {
if (!this.input.trim()) return;
await this.structuredObject.submit(this.input);
}
}
`$3
`typescript
interface StructuredObjectOptions {
/* API endpoint /
api: string; /* Zod schema /
schema: SCHEMA;
/* Unique identifier /
id?: string;
/* Initial object value /
initialValue?: DeepPartial;
/* Completion callback /
onFinish?: (event: {
object: RESULT | undefined;
error: Error | undefined;
}) => void;
/* Error handler /
onError?: (error: Error) => void;
/* Custom fetch function /
fetch?: FetchFunction;
/* Request headers /
headers?: Record;
/* Request credentials /
credentials?: RequestCredentials;
}
`$3
-
object: DeepPartial - Generated object
- loading: boolean - Generation state
- error: Error | undefined - Error state$3
`typescript
// Submit input for generation
await structuredObject.submit(input: unknown);// Stop generation
structuredObject.stop();
`Server Implementation
$3
`typescript
import { openai } from '@ai-sdk/openai';
import { convertToModelMessages, streamText } from 'ai';
import express from 'express';const app = express();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
const { messages, selectedModel } = req.body;
const result = streamText({
model: openai(selectedModel || 'gpt-4o'),
messages: convertToModelMessages(messages),
});
result.pipeUIMessageStreamToResponse(res);
});
`$3
`typescript
app.post('/api/completion', async (req, res) => {
const { prompt } = req.body; const result = streamText({
model: openai('gpt-4o'),
prompt,
});
result.pipeTextStreamToResponse(res);
});
`$3
`typescript
import { streamObject } from 'ai';
import { z } from 'zod';app.post('/api/analyze', async (req, res) => {
const input = req.body;
const result = streamObject({
model: openai('gpt-4o'),
schema: z.object({
title: z.string(),
summary: z.string(),
tags: z.array(z.string()),
sentiment: z.enum(['positive', 'negative', 'neutral']),
}),
prompt:
Analyze this content: ${JSON.stringify(input)},
}); result.pipeTextStreamToResponse(res);
});
`Development Setup
$3
`bash
Install dependencies
pnpm installBuild library
pnpm buildWatch mode
pnpm build:watchRun tests
pnpm testTest watch mode
pnpm test:watch
`$3
`bash
Navigate to example
cd examples/angular-chatSet up environment
echo "OPENAI_API_KEY=your_key_here" > .envStart development (Angular + Express)
pnpm start
`Starts:
- Angular dev server:
http://localhost:4200
- Express API server: http://localhost:3000
- Proxy routes /api/* to ExpressTesting
$3
`bash
pnpm test # Run all tests
pnpm test:watch # Watch mode
pnpm test:update # Update snapshots
`TypeScript Support
Full type safety with automatic type inference:
`typescript
import { Chat, UIMessage, StructuredObject } from '@ai-sdk/angular';
import { z } from 'zod';// Custom message types
interface CustomMessage extends UIMessage {
customData?: string;
}
const chat = new Chat({});
// Schema-typed objects
const schema = z.object({
name: z.string(),
age: z.number(),
});
const obj = new StructuredObject({
api: '/api/object',
schema, // Type automatically inferred
});
// obj.object has type: { name?: string; age?: number } | undefined
`Error Handling
All components provide reactive error states:
`typescript
const chat = new Chat({
onError: (error) => {
console.error('Chat error:', error);
}
});// Template
@if (chat.error) {
{{ chat.error.message }}
}
`Performance
$3
`typescript
chat.stop();
completion.stop();
structuredObject.stop();
`$3
Uses Angular signals for efficient reactivity:
`typescript
// These trigger minimal change detection
chat.messages; // Signal
chat.status; // Signal
chat.error; // Signal
``Apache-2.0