Vercel AI SDK
`@ai-sdk/openai-compatible` 로 붙인다
Vercel AI SDK 는 OpenAI 호환 엔드포인트용 provider 를 따로 제공합니다. 우리 게이트웨이는 그 규격을 그대로 따르므로 @ai-sdk/openai-compatible 로 붙이는 것이 가장 깔끔합니다.
npm install ai @ai-sdk/openai-compatible zod
export MYIP_API_KEY="sk-mo-v1-..."Provider 설정
lib/myip.ts 같은 파일 하나에 provider 를 만들어 두고 앱 전체에서 재사용합니다.
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
export const myip = createOpenAICompatible({
name: 'myip-openrouter',
baseURL: 'https://openrouter.myip.co.kr/api/v1',
apiKey: process.env.MYIP_API_KEY,
headers: {
// 선택 사항. 앱별 사용량 구분에 씁니다 (/docs/app-attribution).
'HTTP-Referer': 'https://example.com',
'X-Title': 'My Example App',
},
});한 번에 받기 — generateText
import { generateText } from 'ai';
import { myip } from '@/lib/myip';
const result = await generateText({
model: myip('google/gemma-4-26b-a4b'),
system: '당신은 간결하게 답하는 도우미입니다.',
prompt: '한국의 수도는?',
temperature: 0.7,
});
console.log(result.text);
console.log(result.usage);스트리밍 — streamText
import { streamText } from 'ai';
import { myip } from '@/lib/myip';
const result = streamText({
model: myip('lgai/exaone-4.0-32b'),
prompt: '짧은 시를 하나 써 줘',
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
console.log('\n', await result.usage);stream_options.include_usage 는 우리가 항상 붙여 보내므로 따로 설정할 필요가 없습니다. 스트림이 끝나면 usage 가 채워집니다.
Next.js Route Handler 예제
app/api/chat/route.ts 에 두면 프런트엔드의 useChat 이 그대로 붙습니다.
import { streamText, convertToModelMessages, type UIMessage } from 'ai';
import { myip } from '@/lib/myip';
export const runtime = 'nodejs';
export const maxDuration = 60;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: myip('google/gemma-4-26b-a4b'),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}비용 확인하기
AI SDK 는 토큰 수까지만 표준화하고, 비용은 provider 고유 값입니다. 우리는 두 경로로 알려줍니다.
1. 응답 헤더 (비스트리밍)
const result = await generateText({
model: myip('google/gemma-4-26b-a4b'),
prompt: '안녕',
});
const headers = result.response.headers ?? {};
console.log(headers['x-myip-cost-krw'], headers['x-myip-currency']);
console.log(headers['x-myip-generation-id']);2. generation 조회 (스트리밍 포함, 항상 동작)
X-MyIP-Generation-Id 로 받은 id 를 정산이 끝난 뒤 조회합니다.
async function costKrw(generationId: string): Promise<number> {
const res = await fetch(
`https://openrouter.myip.co.kr/api/v1/generation?id=${generationId}`,
{ headers: { Authorization: `Bearer ${process.env.MYIP_API_KEY}` } },
);
const body = await res.json();
return body.data.total_cost; // 원 단위
}total_cost 는 크레딧 원장에 기입된 금액과 정확히 같습니다. 계산 방식은 요금 계산 방식 을 보세요.
툴 콜링
import { generateText, tool } from 'ai';
import { z } from 'zod';
import { myip } from '@/lib/myip';
const result = await generateText({
model: myip('google/gemma-4-26b-a4b'),
prompt: '서울 날씨 알려줘',
tools: {
weather: tool({
description: '도시의 현재 날씨를 조회한다',
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, tempC: 21 }),
}),
},
});
console.log(result.text);모델마다 툴 콜링 지원 여부가 다릅니다. GET /api/v1/models 응답의 supported_parameters 에 tools 가 있는지 확인하세요. 자세한 내용은 툴 콜링 에 있습니다.
모델 폴백
우리 고유 확장인 models 배열은 AI SDK 표준 옵션이 아니므로 provider 옵션으로 넘깁니다.
const result = await generateText({
model: myip('lgai/exaone-4.0-32b'),
prompt: '안녕',
providerOptions: {
'myip-openrouter': {
models: ['lgai/exaone-4.0-32b', 'google/gemma-4-26b-a4b'],
},
},
});키 이름은 createOpenAICompatible 에 준 name 과 같아야 합니다. 모델 폴백 참고.
쓸 수 없는 것
embed,embedMany— 임베딩 엔드포인트가 없습니다generateImage,transcribe,generateSpeech— 이미지·음성 엔드포인트가 없습니다
호출하면 404 not_supported 가 돌아옵니다. 지원하지 않는 엔드포인트 를 보세요.
@ai-sdk/openai 의 Responses API 경로도 지원하지 않으므로, 반드시 @ai-sdk/openai-compatible 를 쓰거나 @ai-sdk/openai 의 Chat Completions 경로를 명시적으로 고르세요.
관련 문서
마지막 수정 2026. 9. 5.