
Product
Introducing Tier 1 Reachability: Precision CVE Triage for Enterprise Teams
Socket’s new Tier 1 Reachability filters out up to 80% of irrelevant CVEs, so security teams can focus on the vulnerabilities that matter.
@ai-sdk/angular
Advanced tools
Angular UI components for the AI SDK v5.
The @ai-sdk/angular
package provides Angular-specific implementations using Angular signals for reactive state management:
npm install @ai-sdk/angular ai
@angular/core
)Real-time conversation interface with streaming support.
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: `
<div class="chat-container">
<div class="messages">
@for (message of chat.messages; track message.id) {
<div class="message" [ngClass]="message.role">
@for (part of message.parts; track $index) {
@switch (part.type) {
@case ('text') {
<div style="white-space: pre-wrap">
{{ part.text }}
@if (part.state === 'streaming') {
<span class="cursor">▮</span>
}
</div>
}
@case ('reasoning') {
<details>
<summary>Reasoning</summary>
<div style="white-space: pre-wrap; opacity: 80%">
{{ part.text }}
</div>
</details>
}
@default {
<code>{{ part | json }}</code>
}
}
}
</div>
}
@if (chat.status === 'submitted') {
<div><em>Waiting...</em></div>
}
</div>
<form [formGroup]="chatForm" (ngSubmit)="sendMessage()">
<input formControlName="userInput" placeholder="Type your message..." />
@if (chat.status === 'ready') {
<button type="submit" [disabled]="!chatForm.valid">Send</button>
} @else {
<button [disabled]="chat.status === 'error'" (click)="chat.stop()">
Stop
</button>
}
</form>
</div>
`,
})
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',
},
},
);
}
}
interface ChatInit<UI_MESSAGE extends UIMessage = UIMessage> {
/** Initial messages */
messages?: UI_MESSAGE[];
/** Custom ID generator */
generateId?: () => string;
/** Maximum conversation steps */
maxSteps?: number;
/** Tool call handler */
onToolCall?: (params: { toolCall: ToolCall }) => Promise<string>;
/** Completion callback */
onFinish?: (params: { message: UI_MESSAGE }) => void;
/** Error handler */
onError?: (error: Error) => void;
/** Custom transport */
transport?: ChatTransport;
}
messages: UIMessage[]
- Array of conversation messagesstatus: 'ready' | 'submitted' | 'streaming' | 'error'
- Current statuserror: Error | undefined
- Current error state// Send a message
await chat.sendMessage(
message: UIMessageInput,
options?: {
body?: Record<string, any>;
headers?: Record<string, string>;
}
);
// Regenerate last assistant message
await chat.regenerate(options?: {
body?: Record<string, any>;
headers?: Record<string, string>;
});
// Add tool execution result
chat.addToolResult({
toolCallId: string;
output: string;
});
// Stop current generation
chat.stop();
// HTML template
<input type="file" multiple (change)="onFileSelect($event)" />
// Component
onFileSelect(event: Event) {
const files = (event.target as HTMLInputElement).files;
if (files) {
this.chat.sendMessage({
text: "Analyze these files",
files: files
});
}
}
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}`);
}
},
});
Single-turn text generation with streaming.
import { Component } from '@angular/core';
import { Completion } from '@ai-sdk/angular';
@Component({
selector: 'app-completion',
template: `
<div>
<textarea
[(ngModel)]="completion.input"
placeholder="Enter your prompt..."
rows="4"
>
</textarea>
<button
(click)="completion.complete(completion.input)"
[disabled]="completion.loading"
>
{{ completion.loading ? 'Generating...' : 'Generate' }}
</button>
@if (completion.loading) {
<button (click)="completion.stop()">Stop</button>
}
<div class="result">
<h3>Result:</h3>
<pre>{{ completion.completion }}</pre>
</div>
@if (completion.error) {
<div class="error">{{ completion.error.message }}</div>
}
</div>
`,
})
export class CompletionComponent {
completion = new Completion({
api: '/api/completion',
onFinish: (prompt, completion) => {
console.log('Completed:', { prompt, completion });
},
});
}
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<string, string>;
/** Request body */
body?: Record<string, any>;
/** Request credentials */
credentials?: RequestCredentials;
}
completion: string
- Generated text (writable)input: string
- Current input (writable)loading: boolean
- Generation stateerror: Error | undefined
- Error stateid: string
- Completion IDapi: string
- API endpointstreamProtocol: 'data' | 'text'
- Stream type// Generate completion
await completion.complete(
prompt: string,
options?: {
headers?: Record<string, string>;
body?: Record<string, any>;
}
);
// Form submission handler
await completion.handleSubmit(event?: { preventDefault?: () => void });
// Stop generation
completion.stop();
Generate structured data with Zod schemas and streaming.
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: `
<div>
<textarea
[(ngModel)]="input"
placeholder="Enter content to analyze..."
rows="4"
>
</textarea>
<button (click)="analyze()" [disabled]="structuredObject.loading">
{{ structuredObject.loading ? 'Analyzing...' : 'Analyze' }}
</button>
@if (structuredObject.object) {
<div class="result">
<h3>Analysis:</h3>
<div><strong>Title:</strong> {{ structuredObject.object.title }}</div>
<div>
<strong>Summary:</strong> {{ structuredObject.object.summary }}
</div>
<div>
<strong>Tags:</strong>
{{ structuredObject.object.tags?.join(', ') }}
</div>
<div>
<strong>Sentiment:</strong> {{ structuredObject.object.sentiment }}
</div>
</div>
}
@if (structuredObject.error) {
<div class="error">{{ structuredObject.error.message }}</div>
}
</div>
`,
})
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);
}
}
interface StructuredObjectOptions<SCHEMA, RESULT> {
/** API endpoint */
api: string;
/** Zod schema */
schema: SCHEMA;
/** Unique identifier */
id?: string;
/** Initial object value */
initialValue?: DeepPartial<RESULT>;
/** 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<string, string>;
/** Request credentials */
credentials?: RequestCredentials;
}
object: DeepPartial<RESULT> | undefined
- Generated objectloading: boolean
- Generation stateerror: Error | undefined
- Error state// Submit input for generation
await structuredObject.submit(input: unknown);
// Stop generation
structuredObject.stop();
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);
});
app.post('/api/completion', async (req, res) => {
const { prompt } = req.body;
const result = streamText({
model: openai('gpt-4o'),
prompt,
});
result.pipeTextStreamToResponse(res);
});
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);
});
# Install dependencies
pnpm install
# Build library
pnpm build
# Watch mode
pnpm build:watch
# Run tests
pnpm test
# Test watch mode
pnpm test:watch
# Navigate to example
cd examples/angular-chat
# Set up environment
echo "OPENAI_API_KEY=your_key_here" > .env
# Start development (Angular + Express)
pnpm start
Starts:
http://localhost:4200
http://localhost:3000
/api/*
to Expresspnpm test # Run all tests
pnpm test:watch # Watch mode
pnpm test:update # Update snapshots
Full type safety with automatic type inference:
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<CustomMessage>({});
// 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
All components provide reactive error states:
const chat = new Chat({
onError: (error) => {
console.error('Chat error:', error);
}
});
// Template
@if (chat.error) {
<div class="error">{{ chat.error.message }}</div>
}
chat.stop();
completion.stop();
structuredObject.stop();
Uses Angular signals for efficient reactivity:
// These trigger minimal change detection
chat.messages; // Signal<UIMessage[]>
chat.status; // Signal<ChatStatus>
chat.error; // Signal<Error | undefined>
Apache-2.0
FAQs
Angular implementation of ai-sdk.
The npm package @ai-sdk/angular receives a total of 542 weekly downloads. As such, @ai-sdk/angular popularity was classified as not popular.
We found that @ai-sdk/angular demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s new Tier 1 Reachability filters out up to 80% of irrelevant CVEs, so security teams can focus on the vulnerabilities that matter.
Research
/Security News
Ongoing npm supply chain attack spreads to DuckDB: multiple packages compromised with the same wallet-drainer malware.
Security News
The MCP Steering Committee has launched the official MCP Registry in preview, a central hub for discovering and publishing MCP servers.