콘텐츠로 이동
ABTO 가이드

연동 가이드

Node / Server JavaScript

서버의 프로바이더 요청을 ABTO Gateway로 보내고 사용자와 기능 컨텍스트를 전달하는 SDK입니다.

서버 SDK백엔드에서 실행 (Calling Key)

Server SDK는 토큰이나 비용을 직접 추정하지 않습니다. 게이트웨이로 가는 요청에 사용자와 기능 컨텍스트를 실어 보내고, 실제 실행 기록은 게이트웨이가 남깁니다.

import type OpenAI from 'openai';
import { initAbto } from '@abto-app/calling';
const abto = initAbto({
abtoApiKey: process.env.ABTO_CALLING_KEY,
gatewayBaseURL: 'https://gateway.abto.app/v1',
providerKeys: {
openai: process.env.OPENAI_API_KEY,
},
environment: 'production',
});
const { data: completion, response } = await abto.withContext(
{
deviceId: 'device-abc',
featureId: 'review.summary',
},
async () => {
const openai = await abto.openai<OpenAI>();
return openai.chat.completions
.create({
model: 'gpt-4.1-mini',
messages: [{ role: 'user', content: '이 상품의 리뷰 12개를 세 줄로 요약해 줘.' }],
})
.withResponse();
},
);
const requestId = response.headers.get('x-abto-request-id');
  • completion: 평소와 같은 OpenAI 응답 본문
  • response: Gateway가 발급한 x-abto-request-id를 읽는 자리
SDK 입력Gateway 헤더의미
abtoApiKeyAuthorization: Bearer …ABTO Calling Key
providerKeys.openaix-abto-key-openai직접 계약한 OpenAI provider key
featureIdx-abto-feature-id기능 ID
deviceIdx-abto-device-id사용자 기기 식별자 (선택)

providerKeys의 성격과 취급:

  • Gateway에 키를 등록하는 설정이 아니라, 서버에 보관한 credential을 요청마다 헤더로 실어 보내는 입력
  • 요청 범위로만 전달. 호출 기록에는 미저장
  • 라우팅된 provider의 키가 없으면 Gateway가 그 요청을 거절
  • 문자열 대신 함수를 주면 요청마다 다시 평가. key rotation과 provider별 credential resolver에 사용

호출자가 넣은 헤더의 처리:

  • abto.openai()가 만든 client에서는 extra_headersx-abto-key-*를 덮어쓰지 마세요.
  • Calling SDK가 호출자의 Authorization, x-abto-key-*, x-abto-feature-id, x-abto-device-id를 제거하고 initAbto의 신뢰된 설정과 context로 다시 구성합니다.
  • Calling SDK 없이 기존 OpenAI SDK를 직접 쓴다면 같은 헤더를 각 요청의 extra_headers에 직접 넣습니다. 직접 연결 예시를 참고하세요.

필드 이름은 SDK 안의 표기이고, 실제 요청에서는 헤더로 나갑니다.

  • featureId: 기능 ID(예: review.summary). x-abto-feature-id로 전송
  • deviceId: x-abto-device-id로 전송. 제품 행동과 잇기 위해 브라우저가 만든 device_id(browserAbto.getIdentity().deviceId)를 백엔드로 받아 그대로 전달
    • 임의의 로그인 id를 넣으면 브라우저의 device_id와 달라 제품 행동·사용자별 고정 배정이 연결되지 않음

식별자를 다루는 규칙:

  • 클라이언트가 보낸 deviceId, featureId는 애플리케이션의 기존 요청 스키마로 검증
  • Calling Key와 provider key는 클라이언트 요청에서 받지 않고 서버 환경 변수나 서버 전용 credential resolver에서만 읽기
  • abtoApiKey는 브라우저 번들, 정적 문서, URL에 넣지 않기

응답을 브라우저·모바일 행동과 잇는 방법:

  • Gateway가 응답에 x-abto-request-id를 발급
  • 위 예제처럼 .withResponse()로 읽고, 백엔드 응답에 그대로 포함해 Browser 또는 Mobile SDK의 LLM trace에 연결
  • 데이터 경로는 현재 OpenAI Chat Completions
  • user 메시지의 인라인 base64 이미지와 PDF 지원
  • Streaming, tool calling, 원격 이미지 URL, audio 등 미지원 필드는 조용히 무시하지 않고 400으로 거절
  • 정확한 필드 목록은 Gateway OpenAI 호환 범위 참고

재시도는 두 층에서 일어납니다

섹션 제목: “재시도는 두 층에서 일어납니다”

재시도 층이 둘이라 maxRetries만으로 provider 호출 횟수가 정해지지 않습니다.

무엇을 세는가누가 정하는가
클라이언트애플리케이션 → Gateway 왕복 횟수공식 OpenAI SDK의 clientOptions.maxRetries
GatewayGateway → provider 호출 횟수Gateway 내장 상한과 node 재시도 정책
const openai = await abto.openai({
clientOptions: { maxRetries: 2 },
});
  • 공식 OpenAI 의미 그대로 최초 요청 이후의 재시도 횟수. 0은 왕복 1회, 1은 왕복 2회
  • Calling SDK는 이 값을 덮어쓰지 않으며 별도의 fallback 재시도 설정도 미제공
  • 설정하지 않으면 공식 OpenAI SDK의 기본값을 그대로 사용
  • baseURLapiKey는 신뢰된 라우팅을 위해 ABTO 설정이 우선하고, 그 밖의 공식 client option은 보존
  • clientOptions.fetch는 버려지지 않고 ABTO wrapper 아래의 실제 전송 구현으로 합성

Gateway 층: 왕복 한 번 안에서의 재시도

섹션 제목: “Gateway 층: 왕복 한 번 안에서의 재시도”

한 번의 왕복 안에서 Gateway가 같은 경로로 다시 시도합니다.

  • 네트워크 재시도: 전송 전 unreachable이 확정된 경우. node 정책과 무관하게 항상, 최대 2회
  • Provider 재시도: 429(rate limit), 500, 502, 503, 504, 529. node 재시도 정책을 켠 경우에만, 최대 2회
  • 두 예산은 독립 가산이므로 한 왕복의 provider 호출은 최대 5회(최초 1회 + 네트워크 2회 + provider 2회)
  • 응답 헤더 x-abto-attempt가 몇 번째 시도의 결과인지 표시

재시도하지 않는 경우:

  • 429 중 크레딧 소진(insufficient_quota). 결정적 실패
  • timeout, 전송 후 단절, 본문 크기 초과, 취소. 이중 실행과 이중 과금 위험
  • 501, 505처럼 결정적인 그 밖의 상태

대기 시간:

  • 지수 백오프 + full jitter. 네트워크는 50ms에서 250ms 상한, provider는 500ms에서 8초 상한
  • provider가 Retry-After를 주면 그 값을 존중하고, 8초를 넘으면 기다리지 않고 즉시 오류를 표면화

ABTO 도입 이전에 쓰던 엔드포인트로 되돌아가는 비상 경로입니다. 그래서 목적지를 fallback.baseURL에 직접 지정해야 하며, 기본값은 없습니다. OpenAI에서 직접 키를 발급받아 공식 SDK를 쓰던 고객이라면 그 주소가 https://api.openai.com/v1입니다.

const abto = initAbto({
abtoApiKey: process.env.ABTO_CALLING_KEY,
providerKeys: { openai: process.env.OPENAI_API_KEY },
fallback: {
// ABTO를 붙이기 전 이 코드가 호출하던 주소를 그대로 적습니다.
baseURL: 'https://api.openai.com/v1',
timeoutMs: 30_000,
onTimeout: false,
},
});

원본 Chat Completions body와 model을 그대로 그 주소로 보내며, Gateway의 provider/model 정책을 클라이언트에서 재현하지 않습니다.

현재 요청을 direct로 보내는 경우

  • 연결 수립 전 실패
  • provider 호출 전 admission 503

현재 요청을 폴백하지 않는 경우

  • timeout과 전송 여부가 모호한 disconnect. Gateway가 이미 provider를 실행했을 가능성
  • provider·transport·internal 오류, 결정적인 4xx429, caller abort, streaming 시작 이후
  • 이때 direct circuit도 열지 않으며, 공식 OpenAI SDK가 재시도하면 다시 Gateway를 호출

설정

  • baseURL: 필수. ABTO 이전에 쓰던 주소. OpenAI 요청 경로와 Authorization: Bearer를 받는 엔드포인트여야 합니다. 이 값 없이 폴백을 켜면 initAbto가 오류를 던집니다
  • timeoutMs: 로컬 dispatcher 대기부터 Gateway 응답 헤더까지의 end-to-end 상한. direct 요청은 OpenAI client의 timeout을 유지
  • onTimeout: true: timeout 난 현재 요청까지 재전송. 중복 실행과 과금 위험을 감수하는 명시적 선택
  • fallback: false: 기능 전체 해제

경계

  • transport는 SDK attempt마다 Gateway 판단과 direct 전송을 각각 한 번만 수행
  • direct 호출은 Gateway policy, ABTO telemetry, request_id를 거치지 않음. model 은 그 엔드포인트가 지원하는 것으로 제한
  • OpenAI에 필요한 header만 전달하고 Cookie, proxy credential, 사용자 정의 Gateway header는 제거
  • direct 전환 뒤의 응답과 오류는 공식 OpenAI SDK로 그대로 반환하며 재시도 여부는 그 SDK 설정이 결정
  • Anthropic과 Gemini key는 Gateway 라우팅 후보일 뿐, 현재 SDK의 네이티브 direct fallback 대상이 아님