DEV Community

ZNY
ZNY

Posted on

DEV.TO ARTICLE 33: TypeScript AI SDK: Building Type-Safe AI-Powered Applications

Target Keyword: "typescript ai sdk tutorial"
Tags: typescript,nodejs,ai,programming,developer
Type: Tutorial


Content

TypeScript AI SDK: Building Type-Safe AI-Powered Applications

TypeScript's type system transforms AI API integration from guesswork into reliable, maintainable code. When you're building production AI applications, TypeScript catches errors at compile time, not runtime. Here's how to build type-safe AI integrations.

Why TypeScript for AI APIs?

  1. Catch errors early — Invalid API responses caught at compile time
  2. IDE autocompletion — Know exactly what the API returns
  3. Refactoring confidence — Change types and the compiler finds all affected code
  4. Documentation — Types serve as living documentation

Type-Safe API Client

import { z } from 'zod';

// Define response schemas
const MessageSchema = z.object({
  role: z.enum(['system', 'user', 'assistant']),
  content: z.string()
});

const ChoiceSchema = z.object({
  message: MessageSchema,
  finish_reason: z.string(),
  index: z.number()
});

const UsageSchema = z.object({
  prompt_tokens: z.number(),
  completion_tokens: z.number(),
  total_tokens: z.number()
});

const ChatCompletionResponseSchema = z.object({
  id: z.string(),
  object: z.string(),
  created: z.number(),
  model: z.string(),
  choices: z.array(ChoiceSchema),
  usage: UsageSchema
});

type ChatMessage = z.infer<typeof MessageSchema>;
type ChatResponse = z.infer<typeof ChatCompletionResponseSchema>;

// Type-safe client
class ClaudeClient {
  constructor(
    private readonly apiKey: string,
    private readonly baseURL: string = 'https://api.ofox.ai/v1'
  ) {}

  async chat(messages: ChatMessage[]): Promise<ChatResponse> {
    const response = await fetch(`${this.baseURL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: 'claude-3-5-sonnet-20241022',
        messages
      })
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    const data = await response.json();

    // Zod validates at runtime AND gives you typed result
    return ChatCompletionResponseSchema.parse(data);
  }
}
Enter fullscreen mode Exit fullscreen mode

Streaming with Types

async function* streamChat(
  client: ClaudeClient,
  messages: ChatMessage[]
): AsyncGenerator<string> {
  const response = await fetch(`${client.baseURL}/chat/completions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${client.apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: 'claude-3-5-sonnet-20241022',
      messages,
      stream: true
    })
  });

  if (!response.body) throw new Error('No response body');

  const decoder = new TextDecoder();

  for await (const chunk of response.body) {
    const lines = decoder.decode(chunk).split('\n');
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        if (data.choices[0].delta.content) {
          yield data.choices[0].delta.content;
        }
      }
    }
  }
}

// Usage with full type safety
for await (const token of streamChat(client, messages)) {
  process.stdout.write(token);
}
Enter fullscreen mode Exit fullscreen mode

Building a Type-Safe Agent Framework

// Tool definitions with full type safety
const ToolSchema = z.object({
  name: z.string(),
  description: "z.string(),"
  parameters: z.record(z.string(), z.unknown())
});

type Tool = z.infer<typeof ToolSchema>;

interface ToolResult<T> {
  success: boolean;
  data?: T;
  error?: string;
}

class TypedAgent {
  constructor(
    private client: ClaudeClient,
    private tools: Tool[]
  ) {}

  async execute(systemPrompt: string, task: string): Promise<string> {
    const messages: ChatMessage[] = [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: task }
    ];

    while (true) {
      const response = await this.client.chat(messages);
      const content = response.choices[0].message.content;

      // Check if agent wants to use a tool
      const toolCall = this.parseToolCall(content);
      if (toolCall) {
        const result = await this.executeTool(toolCall);
        messages.push({ role: 'assistant', content });
        messages.push({
          role: 'user',
          content: `Tool result: ${JSON.stringify(result)}`
        });
      } else {
        return content;
      }
    }
  }

  private parseToolCall(content: string): { name: string; args: unknown } | null {
    // Parse tool call from response
    const match = content.match(/<tool_call>([\s\S]*?)<\/tool_call>/);
    if (!match) return null;
    return JSON.parse(match[1]);
  }

  private async executeTool(call: { name: string; args: unknown }): Promise<ToolResult<unknown>> {
    const tool = this.tools.find(t => t.name === call.name);
    if (!tool) return { success: false, error: `Unknown tool: ${call.name}` };

    try {
      // Tool execution would be implemented here
      return { success: true, data: null };
    } catch (e) {
      return { success: false, error: (e as Error).message };
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Validation with Zod

Zod validates API responses at runtime:

import { z } from 'zod';

// Define the exact shape you expect
const APIResponse = z.object({
  model: z.string(),
  id: z.string(),
  choices: z.array(z.object({
    message: z.object({
      role: z.string(),
      content: z.string()
    }),
    finish_reason: z.string()
  }))
});

async function safeChat(client: ClaudeClient, messages: ChatMessage[]) {
  try {
    const raw = await client.chatRaw(messages); // Returns unknown
    const validated = APIResponse.parse(raw); // Throws if shape doesn't match
    return validated;
  } catch (error) {
    if (error instanceof z.ZodError) {
      console.error('API response validation failed:', error.issues);
      throw new Error('Unexpected API response format');
    }
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Getting Started

Build type-safe AI applications with TypeScript and ofox.ai — their OpenAI-compatible API integrates seamlessly with TypeScript's type system. Sign up and start building.

👉 Get started with ofox.ai


This article contains affiliate links.


Tags: typescript,nodejs,ai,programming,developer
Canonical URL: https://dev.to/zny10289

Top comments (0)