29 de maio de 202610 min de leitura

Estendendo o Vercel AI SDK para o Microsoft Foundry (TypeScript)

O Vercel AI SDK oferece uma única superfície elegante e agnóstica de provedor — generateText, streamText, tools e structured output — para construir aplicações agentic. O provider oficial @ai-sdk/azure, no entanto, cobre apenas o chat da Azure OpenAI, deixando de fora o restante do catálogo do Microsoft Foundry: Llama, DeepSeek, Mistral, Phi e agora Anthropic Claude.

TL;DR: O Vercel AI SDK oferece uma camada única e agnóstica para construir agentes, mas o provider oficial para Azure só cobre Azure OpenAI. Este artigo mostra como criar dois adapters — um compatível com OpenAI e outro com Anthropic — para acessar todo o catálogo do Microsoft Foundry (Llama, DeepSeek, Mistral, Claude etc.) sem modificar o código de aplicação. A conclusão prática: é possível integrar Foundry de forma limpa, mantendo a portabilidade do AI SDK e usando autenticação via Entra ID ou API key.

Qual a justificativa técnica?

Se você já entregou agentes com o Vercel AI SDK, conhece o apelo: uma superfície limpa com generateText, streamText e tools que permite trocar modelos e provedores sem tocar na lógica de orquestração. Essa portabilidade é o ponto central.

O problema é que, ao mover esses agentes para o Azure, o provider oficial @ai-sdk/azure fica restrito à chat API da Azure OpenAI. Ele constrói URLs como https://{resourceName}.openai.azure.com/openai/v1{path} e autentica especificamente para Azure OpenAI. Portanto, se você quiser usar modelos do catálogo Foundry como Llama, DeepSeek, Mistral, Phi, Anthropic Claude e outros, um provider nativo "drop‑in" não está disponível.

O Microsoft Foundry foi projetado para atuar como um backplane multi‑modelo único, com um endpoint e um conjunto de credenciais que podem atender modelos de vários provedores. Ou seja, a dificuldade não é "como alcançar os modelos do Azure?", mas "como conectar o Foundry de forma limpa ao AI SDK para que o código existente do agente permaneça exatamente o mesmo?

Como funciona a arquitetura de alto nível?

A extensão introduz dois adapters de provedor que se posicionam entre a superfície agnóstica do AI SDK e as duas rotas paralelas do Foundry, baseadas no seguinte:

  1. O Foundry expõe modelos de muitos provedores por meio de um endpoint + uma credencial e aceita Chat Completions no estilo OpenAI para implantações OpenAI e não‑OpenAI quando você acessa /openai/v1/.
  2. O Claude fica em uma rota paralela /anthropic/v1/, falando a Anthropic Messages API.
Adapter Rota Foundry Protocolo Provider AI SDK
Adapter compatível com OpenAI /openai/v1/* OpenAI Chat Completions / Responses @ai-sdk/openai-compatible
Adapter Anthropic /anthropic/v1/* Anthropic Messages API @ai-sdk/anthropic (com baseURL e headers customizados)

Ambos os adapters compartilham as mesmas opções de autenticação (Entra ID como padrão, API key como fallback) e o mesmo padrão de roteamento de modelo (nome da deployment como modelId). O código no nível da aplicação (agentes, planners, routers, pipelines RAG) permanece inalterado.

Modelos OpenAI e OSS no Foundry via /openai/v1

As famílias de modelos Azure OpenAI (GPT‑4.1, GPT‑5.x, série o, etc.), além de Llama, DeepSeek, Mistral, Phi e modelos parceiros, são acessíveis pelo caminho compatível com OpenAI do Foundry (/openai/v1).

// providers/foundry-openai.ts
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { DefaultAzureCredential } from '@azure/identity';
const credential = new DefaultAzureCredential();

// Scope for OpenAI-compatible surface on Foundry
const FOUNDRY_AI_SCOPE = 'https://cognitiveservices.azure.com/.default';

// Provider configuration — endpoint targeting and auth selection. Pass either `resource` (shortcut) or `baseURL` (full override). Auth defaults to Entra ID; set `apiKey`.
export interface FoundryOpenAIOptions {
    resource?: string; // Foundry resource name. Used to build the default base URL. 
  baseURL?: string; // Full base URL override,
 // "https://<resource>.services.ai.azure.com/openai/v1". 
    tenantId?: string; // Optional Entra tenant ID where the Foundry resource lives.
   apiKey?: string;// Optional Foundry resource API key for key based authentication
}
export function createFoundry(options: FoundryOpenAIOptions = {}) {
  const baseURL =
    options.baseURL ??
    (options.resource
      ? `https://${options.resource}.services.ai.azure.com/openai/v1`
      : process.env.FOUNDRY_RESOURCE
        ? `https://${process.env.FOUNDRY_RESOURCE}.services.ai.azure.com/openai/v1`
        : undefined);
  if (!baseURL) {
    throw new Error(
      'createFoundry: provide `resource`, `baseURL`, or set FOUNDRY_RESOURCE env var.',
    );
  }
  
  // Auth mode A (default) — Entra ID via DefaultAzureCredential.
  
  const credential = new DefaultAzureCredential(
    options.tenantId ? { tenantId: options.tenantId } : undefined,
  );
  return createOpenAICompatible({
    name: 'microsoft-foundry',
    baseURL,
    apiKey: 'placeholder-not-used',
    fetch: async (input, init) => {
      const token = (await credential.getToken(FOUNDRY_AI_SCOPE))?.token;
      if (!token) throw new Error('Failed to acquire Entra ID token for Foundry');
      const headers = new Headers(init?.headers);
      headers.set('Authorization', `Bearer ${token}`);
      return fetch(input, { ...init, headers });
    },
  });
} 

Exemplo de código para testar o providers/foundry-openai.ts em três modelos diferentes (Azure OpenAI, DeepSeek e Moonshot AI):

import { generateText, streamText } from 'ai';
import { createFoundry } from './providers/foundry-openai';

// Pass the Foundry resource name (or a full baseURL) here.
const foundry = createFoundry({
  resource: {Your Foundry Resource name},
  // baseURL: 'https://{Your Foundry Resource name}.services.ai.azure.com/openai/v1', // alternative
});

// One AI SDK surface, three different Foundry models, zero transport changes.
const gpt   = foundry.chatModel('gpt-4.1-mini');
const ds    = foundry.chatModel('DeepSeek-V3.1');
const kimi = foundry.chatModel('Kimi-K2.6');
//const llama = foundry.chatModel('Llama-3.3-70B-Instruct');

// Simple generation
const { text } = await generateText({
  model: gpt,
  prompt: 'Say hello in one short sentence.',
});
console.log('[gpt-4.1-mini]', text);
const { text: dsText } = await generateText({
  model: ds,
  prompt: 'What is 2 + 2?',
});
console.log('[DeepSeek-V3.1]', dsText);

// Streaming
process.stdout.write('[kimi] ');
const stream = streamText({ model: kimi, prompt: 'Draft a one-line customer reply.' });
for await (const chunk of stream.textStream) process.stdout.write(chunk);
process.stdout.write('\n');

Se você preferir usar API key (apenas em dev ou não‑produção), utilize o seguinte código:

  // Auth mode B — API key (used when `apiKey` is set or AZURE_FOUNDRY_API_KEY is in the environment). Foundry accepts the resource key via the
  // `api-key` header on the OpenAI-compatible route.
  
  const apiKey = options.apiKey ?? process.env.AZURE_FOUNDRY_API_KEY;
  if (apiKey) {
    return createOpenAICompatible({
      name: 'microsoft-foundry',
      baseURL,
      apiKey: 'placeholder-not-used',
      fetch: async (input, init) => {
        const headers = new Headers(init?.headers);
        headers.delete('Authorization');
        headers.set('api-key', apiKey);
        return fetch(input, { ...init, headers });
      },
    });
  }

Anthropic Claude no Foundry via /anthropic/v1

O Claude no Foundry não fica atrás de /openai/v1. Ele é exposto em https://{resource}.services.ai.azure.com/anthropic/v1/* e fala a Anthropic Messages API, não o formato Chat Completions do OpenAI. Portanto, @ai-sdk/openai-compatible não pode ser usado; em vez disso, utilize @ai-sdk/anthropic.

import { createAnthropic } from "@ai-sdk/anthropic";
import { DefaultAzureCredential } from "@azure/identity";

const SCOPE = "https://cognitiveservices.azure.com/.default";

export interface AnthropicFoundryOptions {
  /** Foundry resource hostname or full URL, e.g. "https://<resource>.services.ai.azure.com" */
  endpoint: string;
  /** Optional Entra tenant ID where the Foundry resource lives */
  tenantId?: string;
  // OPTIONAL: Key-based authentication (uncomment to use API key instead of Entra ID). If `apiKey` is provided, it will be used directly and  //DefaultAzureCredential will be skipped.

  // apiKey?: string;

export function createAnthropicFoundry(options: AnthropicFoundryOptions) {
  const url = new URL(options.endpoint);
  const baseURL = `${url.protocol}//${url.host}/anthropic/v1`;
  const credential = new DefaultAzureCredential(
    options.tenantId ? { tenantId: options.tenantId } : undefined,
  );
  return createAnthropic({
    baseURL,
    // apiKey is required by the provider but unused; overriding the Authorization header per-request via custom fetch.
    apiKey: "placeholder-not-used",
    fetch: async (input, init) => {
      // Entra ID (default) — acquire a bearer token via DefaultAzureCredential.
      const token = (await credential.getToken(SCOPE))?.token;
      if (!token) throw new Error("Failed to acquire Entra ID token");

      // OPTION B: Key-based authentication (uncomment to use API key). Foundry accepts the resource key via the `api-key` header.
      // Comment out the Entra block above when enabling this.
      // const apiKey = options.apiKey ?? process.env.AZURE_FOUNDRY_API_KEY;
      // if (!apiKey) throw new Error("Missing Foundry API key");
      const headers = new Headers(init?.headers);
      // Strip Anthropic's default API key header and replace with bearer token
      headers.delete("x-api-key");
      headers.set("Authorization", `Bearer ${token}`);
      // For key-based auth, replace the line above with:
      // headers.set("api-key", apiKey);
      return fetch(input, { ...init, headers });
    },
  });
}
}

Exemplo de teste para o modelo Claude:

// Test using AI-sdk/anthropic against a Microsoft Foundry endpoint with Entra ID.
import { generateText } from "ai";
import { createAnthropicFoundry } from "./providers/anthropic-foundry";

const anthropic = createAnthropicFoundry({
  endpoint: "https://<your-foundry-resource>.services.ai.azure.com",
  tenantId: process.env.AZURE_TENANT_ID, // optional (only needed if multi-tenant)

  // OPTIONAL: Key-based authentication. Uncomment to use an API key instead of Entra ID (DefaultAzureCredential). Requires enabling the
  // matching `apiKey` branch in providers/anthropic-foundry.ts.
  // apiKey: process.env.AZURE_FOUNDRY_API_KEY,
});
const { text } = await generateText({
  model: anthropic("claude-opus-4-7"), // replace with your Foundry model deployment name 
  prompt: "Hello!",
});
console.log(text);

Essa abordagem permite conexões perfeitas entre sistemas, garantindo flexibilidade, escalabilidade e segurança.

Resumo

Categoria de Modelo no MS Foundry Tipo de Endpoint Utilizado Padrão de Integração com Vercel AI SDK Opções de Autenticação
Modelos compatíveis com OpenAI (OpenAI, DeepSeek, Moonshot AI, Mistral AI, Meta etc.) Endpoint /openai/v1 compatível com OpenAI Uso nativo do SDK (funciona com override de baseURL) • API Key • Microsoft Entra ID (token)
Anthropic Claude Endpoint Anthropic Messages API Usa padrão de provider (não é compatível com OpenAI) • API Key • Microsoft Entra ID (token)

Próximos passos

Pronto para começar a construir? Aqui estão seus caminhos:

Perguntas Frequentes

  • Por que o provider oficial do AI SDK para Azure não funciona com Foundry?
    O provider oficial @ai-sdk/azure foi construído exclusivamente para o endpoint de chat do Azure OpenAI, usando URLs como https://{resource}.openai.azure.com/openai/v1/.... O Microsoft Foundry expõe modelos de terceiros (Llama, DeepSeek, Claude etc.) em endpoints diferentes, como /openai/v1/ e /anthropic/v1/, que exigem adaptadores separados.

  • Quais modelos podem ser acessados via o adapter OpenAI?
    Modelos da família Azure OpenAI (GPT-4.1, GPT-5.x, o‑series), Llama, DeepSeek, Mistral, Phi e outros modelos de parceiros que seguem o formato Chat Completions do OpenAI — todos disponíveis no caminho /openai/v1/ do Foundry.

  • Como autenticar contra o Foundry usando o adapter?
    O suporte padrão é via Entra ID (token Bearer obtido com DefaultAzureCredential), mas também é possível usar API key (recomendado apenas para ambientes de desenvolvimento ou não‑produção). Ambos os adapters aceitam as duas opções.

  • O código da aplicação precisa ser alterado para usar os adapters?
    Não. Os adaptadores implementam a interface padrão do Vercel AI SDK (createOpenAICompatible e createAnthropic). O código de geração de texto, streaming e ferramentas permanece exatamente o mesmo; apenas a configuração do provider é substituída.

  • O Claude no Foundry segue o mesmo padrão dos outros modelos?
    Não. O Claude é exposto em um endpoint separado (/anthropic/v1/) que usa a Anthropic Messages API, não o formato OpenAI. Por isso, o adapter para Claude utiliza o pacote @ai-sdk/anthropic com configuração customizada de baseURL e headers.


Artigo originalmente publicado em Azure Updates - Latest from Azure Charts.

Gostou? Compartilhe:
Precisa de ajuda?Fale com nossos especialistas 👋
Avatar Walcew - Headset