AI Connect/Reference/TypeScript

TypeScript

VinkiusについてAIに質問

クライアント設定に型を付け、ケイパビリティの検索を明示的に保ち、2 つの実行結果を表現し、unknown からスローされる値を絞り込みます。

SDK には TypeScript の型が同梱されています。ここで紹介するパターンは、型システムを開発者の味方として働かせ続けるためのものです。

構築前にクライアント設定に型を付ける

typescript
import { Vinkius } from '@vinkius/connect';
import type { VinkiusOptions } from '@vinkius/connect';

const options = {
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!,
  timeoutMs: 20_000,
  maxRetries: 2,
  hooks: {
    onResponse: ({ status, requestId }) => {
      console.debug({ status, requestId });
    },
  },
} satisfies VinkiusOptions;

export const vinkius = new Vinkius(options);

satisfies は、オブジェクトがコンストラクターに渡される前に VinkiusOptions へ拡大されることなく、プロパティ名とコールバック引数を検証します。

公開契約をルートからインポートする

typescript
import {
  Capability,
  CapabilitySet,
  ValidationError,
  Vinkius,
  VinkiusError,
} from '@vinkius/connect';

import type {
  CapabilityQuery,
  CapabilityResult,
  ConnectorStatus,
  CredentialSchema,
  CredentialStatus,
  ExecuteOptions,
  JSONSchema,
  RequestOptions,
} from '@vinkius/connect';

クラスとエラーはランタイムの値であるため、newinstanceof を使用する場合は通常どおりインポートします。TypeScript の設定で imports が保持される場合は、インターフェースやエイリアスに import type を使用してください。

ランタイムのケイパビリティ検索を明示的に保つ

typescript
import type { Capability, CapabilityResult } from '@vinkius/connect';

async function executeIssue(
  externalId: string,
  operationId: string,
): Promise<CapabilityResult> {
  const query = {
    include: ['github'],
  } satisfies CapabilityQuery;

  const capabilities = await vinkius.user(externalId).capabilities(query);
  const capability: Capability | undefined =
    capabilities.findCapability('github__create_issue');

  if (!capability) {
    throw new Error('GitHub create_issue is not available for this user');
  }

  return capability.execute(
    { owner: 'acme', repo: 'product', title: 'Document the SDK' },
    { idempotencyKey: `create-issue:${operationId}` },
  );
}

ケイパビリティの可用性はユーザーとサービスのレスポンスに依存するため、検索は Capability | undefined を返します。非 null アサーションでこの分岐を隠さないでください。

2 つの実行結果を表現する

typescript
interface CompletedAction {
  kind: 'completed';
  text: string;
}

interface FailedAction {
  kind: 'capability-error';
  text: string;
}

type ActionOutcome = CompletedAction | FailedAction;

function mapResult(result: CapabilityResult): ActionOutcome {
  const text = result.content.map((part) => part.text).join('\n');
  return result.isError
    ? { kind: 'capability-error', text }
    : { kind: 'completed', text };
}

SDK の型では isError はブール値であり、判別用のリテラルではありません。後続のコードで網羅的な switch の恩恵を受けられる場合は、独自のユニオン型にマッピングしてください。

unknown からスローされる値を絞り込む

typescript
import {
  RateLimitError,
  ValidationError,
  VinkiusError,
} from '@vinkius/connect';

type RequestFailure =
  | { kind: 'validation'; fields: unknown }
  | { kind: 'rate-limit'; retryAfterMs?: number }
  | { kind: 'sdk'; code: string; status: number; requestId?: string };

function classifyFailure(error: unknown): RequestFailure | undefined {
  if (error instanceof ValidationError) {
    return { kind: 'validation', fields: error.errors };
  }

  if (error instanceof RateLimitError) {
    return {
      kind: 'rate-limit',
      ...(error.retryAfterMs !== undefined
        ? { retryAfterMs: error.retryAfterMs }
        : {}),
    };
  }

  if (error instanceof VinkiusError) {
    return {
      kind: 'sdk',
      code: error.code,
      status: error.status,
      ...(error.requestId ? { requestId: error.requestId } : {}),
    };
  }

  return undefined;
}

不明な値のための分岐を残してください。adapter のディスパッチャー、アプリケーションの hooks、一部のアボートのタイミングに関わるパスは、VinkiusError 階層外の値をスローすることがあります。

スキーマをランタイムの契約として扱う

typescript
function requiredCredentialKeys(schema: CredentialSchema): string[] {
  return Object.entries(schema)
    .filter(([, field]) => field.required === true)
    .map(([key]) => key);
}

function capabilitySchema(capability: Capability): JSONSchema {
  return capability.inputSchema;
}

CredentialSchema は型付きのフィールド記述子を持ちます。JSONSchema はコネクタのスキーマが異なるため、意図的に Record<string, unknown> になっています。ツールの引数をドメイン固有の型として扱う前に、アプリケーションが選択した JSON Schema ライブラリで検証してください。モデルの任意の出力を直接キャストすることは避けてください:

typescript
// Avoid: the cast performs no runtime validation.
const issue = modelArgs as { owner: string; repo: string; title: string };

代わりに、modelArgscapability.inputSchema に対して検証し、その後に検証済みの値をアプリケーションの型へマッピングしてください。

リクエスト制御を別々に型付けする

typescript
const requestOptions: RequestOptions = {
  signal: request.signal,
};

const executeOptions: ExecuteOptions = {
  signal: request.signal,
  idempotencyKey: `operation:${operationId}`,
};

すべてのリクエストメソッドは signal を受け付けます。idempotencyKey を追加するのはケイパビリティの実行だけです。adapter のディスパッチヘルパーは、返されるまたはバインドされる実行関数が型付きであっても、ExecuteOptions を受け付けません。

adapter でファクトリの戻り値の型を保持する

ファクトリ adapter はジェネリックであり、注入されたファクトリが作成したものを返します:

typescript
import { toLangChainTools } from '@vinkius/connect/langchain';
import type { JSONSchema } from '@vinkius/connect';

interface AppTool {
  name: string;
  run(input: Record<string, unknown>): Promise<string>;
}

const toolFactory = (
  fn: (input: Record<string, unknown>) => Promise<string>,
  config: { name: string; description: string; schema: JSONSchema },
): AppTool => ({
  name: config.name,
  run: fn,
});

const tools: AppTool[] = toLangChainTools(capabilities, {
  tool: toolFactory,
});

この例は adapter の構造的契約のみを使用しています。実際のフレームワークのファクトリを、アプリケーションがインストールしたバージョンに対してテストしてください。

次のステップ