
Security News
Google’s OSV Fix Just Added 500+ New Advisories — All Thanks to One Small Policy Change
A data handling bug in OSV.dev caused disputed CVEs to disappear from vulnerability feeds until a recent fix restored over 500 advisories.
@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 1,327 weekly downloads. As such, @ai-sdk/angular popularity was classified as 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 4 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.
Security News
A data handling bug in OSV.dev caused disputed CVEs to disappear from vulnerability feeds until a recent fix restored over 500 advisories.
Research
/Security News
175 malicious npm packages (26k+ downloads) used unpkg CDN to host redirect scripts for a credential-phishing campaign targeting 135+ organizations worldwide.
Security News
Python 3.14 adds template strings, deferred annotations, and subinterpreters, plus free-threaded mode, an experimental JIT, and Sigstore verification.