AI Connect/Reference/レシピ
レシピ
コネクタの設定、直接実行、モデルに渡すツールの絞り込み、キャンセル、トレース、カタログスキーマのキャッシュのためのパターンを紹介します。
これらのレシピは SDK の公開サーフェスのみを使用し、ユーザースコープを明示します。すべての関心事を 1 つのルートにまとめるのではなく、アプリケーションに必要なものを組み合わせてください。
サーバー側クライアントを再利用する
// lib/vinkius.ts
import { Vinkius } from '@vinkius/connect';
export const vinkius = new Vinkius({
appId: process.env.VINKIUS_APP_ID!,
apiKey: process.env.VINKIUS_APP_KEY!,
timeoutMs: 20_000,
maxRetries: 2,
});クライアントは現在のユーザーではなく、アプリケーションの設定を保持します。受信リクエストごとに externalId を導出してください。
カタログスキーマからコネクタフォームを構築する
import type { CredentialField, CredentialSchema } from '@vinkius/connect';
import { vinkius } from './lib/vinkius';
interface FormField {
key: string;
label: string;
required: boolean;
type: CredentialField['type'];
docsUrl?: string;
}
async function connectorForm(slug: string): Promise<FormField[]> {
const schema: CredentialSchema = await vinkius
.user('schema-only-placeholder')
.connector(slug)
.credentials.schema();
return Object.entries(schema).map(([key, field]) => ({
key,
label: field.label ?? key,
required: field.required ?? false,
type: field.type,
...(field.docs_url ? { docsUrl: field.docs_url } : {}),
}));
}スキーマの照会はカタログスコープであり、実際の接続は不要です。上記のユーザーハンドルはリクエストを一切送信せず、fluent なスキーマメソッドに到達するためだけに使用されます。vinkius.catalog.get(slug).credential_schema が直接的な低レベルの代替手段です。
認証済みルートからコネクタの資格情報を保存する
async function saveConnector(
externalId: string,
slug: string,
values: Record<string, string>,
) {
const connector = vinkius.user(externalId).connector(slug);
await connector.connect();
const state = await connector.credentials.set(values);
return {
configured: state.configured,
status: await connector.status(),
};
}この関数の前に externalId を認証・認可してください。保存された値は返されず、結果には設定済みキーのフラグが含まれます。
接続の健全性を表示する
const connections = await vinkius.user(externalId).connectors();
const view = connections.map((connection) => ({
slug: connection.slug,
status: connection.status,
action:
connection.status === 'ready'
? 'use'
: connection.status === 'needs_credentials'
? 'update-credentials'
: 'review-connection',
}));connectors() は既存の接続を一覧表示します。ユーザーが一度も接続したことのないカタログのコネクタを追加することはありません。
モデルなしでアクションを実行する
import type { CapabilityResult } from '@vinkius/connect';
async function createIssue(
externalId: string,
input: { owner: string; repo: string; title: string },
operationId: string,
): Promise<CapabilityResult> {
const capabilities = await vinkius.user(externalId).capabilities({
include: ['github'],
});
const capability = capabilities.findCapability('github__create_issue');
if (!capability) {
throw new Error('GitHub create_issue is not available for this user');
}
return capability.execute(input, {
idempotencyKey: `create-issue:${operationId}`,
});
}直接実行は CapabilityResult を保持し、ExecuteOptions を受け付けます。これは adapter のディスパッチヘルパーとは異なる点です。
モデルに必要最小限のコネクタセットを与える
const issueTools = await vinkius.user(externalId).capabilities({
include: ['github'],
});
if (issueTools.length === 0) {
return { tools: [], requiresConnection: true };
}include はサーバー側でフィルタリングします。集計後にローカルで除外するには exclude を、既存の 1 つの接続を明示的に対象としたい場合は connector(slug).capabilities() を使用してください。セットの変換はモデルの呼び出し時のみ行います:
import { toJSONSchemaTools } from '@vinkius/connect/json-schema';
const definitions = toJSONSchemaTools(issueTools);実行には issueTools を保持してください。変換された定義にはユーザースコープのルーティングプロパティは含まれません。
呼び出し側のキャンセルを追加する
async function loadCapabilitiesWithBudget(externalId: string) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort('application budget exceeded'), 5_000);
try {
return await vinkius.user(externalId).capabilities({
signal: controller.signal,
});
} finally {
clearTimeout(timer);
}
}呼び出し側のシグナルはクライアントの試行ごとのタイムアウトと組み合わされます。クライアントの切断後に処理を中断することが重要な場合は、受信リクエストのシグナルを渡してください。ファクトリにバインドされた adapter の実行やディスパッチヘルパーはこのシグナルを受け付けません。キャンセルを実行まで届かせる必要がある場合は、Capability.execute() を直接呼び出してください。
試行とリクエスト ID をトレースする
const vinkius = new Vinkius({
appId: process.env.VINKIUS_APP_ID!,
apiKey: process.env.VINKIUS_APP_KEY!,
hooks: {
onRequest: ({ method, url }) => {
console.debug('Vinkius request attempt', { method, url });
},
onResponse: ({ status, requestId }) => {
console.debug('Vinkius response', { status, requestId });
},
},
});フックは HTTP 試行ごとに 1 回実行されるため、エントリの繰り返しは自動再試行を表す可能性があります。onRequest にはボディがありません。onResponse は固定名でマスキングされたレスポンスボディのコピーを受け取ります。コールバックは同期的に保ち、例外をスローせず、無制限の処理を行わないでください。
機密情報を含まないカタログスキーマをキャッシュする
import { ResolverCache } from '@vinkius/connect';
import type { CredentialSchema } from '@vinkius/connect';
const catalogCache = new ResolverCache(10 * 60 * 1000);
async function credentialSchema(slug: string): Promise<CredentialSchema> {
return catalogCache.resolve(`credential-schema:${slug}`, async () => {
const detail = await vinkius.catalog.get(slug);
return detail.credential_schema;
});
}ResolverCache はスタンドアロンであり、クライアントが自動的に使用することはありません。同時発生するキャッシュミスはまとめられず、期限切れのエントリは読み取り時に削除され、拒否された計算はキャッシュされません。資格情報の値、ベアラートークン、認可の判断をこのキャッシュに保存しないでください。
