AI Connect/Reference/Receitas
Receitas
Aplique padrões focados para configuração de conectores, execução direta, ferramentas restritas para modelos, cancelamento, rastreamento e cache do schema do catálogo.
Estas receitas usam apenas a superfície pública do SDK e deixam explícito o escopo do usuário. Combine as que sua aplicação precisa em vez de colocar todas as preocupações em uma única rota.
Reutilize um cliente no servidor
// 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,
});O cliente armazena a configuração da aplicação, não um usuário atual. Derive externalId para cada requisição recebida.
Crie um formulário de conector a partir do schema do catálogo
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 } : {}),
}));
}A consulta do schema tem escopo de catálogo e não exige uma conexão real. O handle de usuário acima não faz nenhuma requisição e é usado apenas para alcançar o método fluente do schema; vinkius.catalog.get(slug).credential_schema é a alternativa direta de baixo nível.
Salve credenciais do conector por uma rota autenticada
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(),
};
}Autentique e autorize externalId antes dessa função. Os valores armazenados não são retornados; o resultado contém indicadores das chaves configuradas.
Renderize o estado das conexões
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() lista as conexões existentes. Ele não acrescenta os conectores do catálogo que o usuário nunca conectou.
Execute uma ação sem um modelo
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}`,
});
}A execução direta preserva CapabilityResult e aceita ExecuteOptions, ao contrário dos helpers de despacho dos adapters.
Forneça ao modelo o menor conjunto de conectores necessário
const issueTools = await vinkius.user(externalId).capabilities({
include: ['github'],
});
if (issueTools.length === 0) {
return { tools: [], requiresConnection: true };
}include filtra no servidor. Use exclude para remoção local depois da agregação, ou connector(slug).capabilities() quando quiser especificamente uma conexão existente. Converta o conjunto somente na chamada ao modelo:
import { toJSONSchemaTools } from '@vinkius/connect/json-schema';
const definitions = toJSONSchemaTools(issueTools);Mantenha issueTools para execução. As definições convertidas não contêm as propriedades de roteamento com escopo por usuário.
Adicione o cancelamento do chamador
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);
}
}O sinal do chamador é combinado com o timeout por tentativa do cliente. Passe o sinal da requisição recebida quando for importante interromper o trabalho depois que o cliente fechar a conexão. Execuções de adapters vinculadas por fábricas e helpers de despacho não aceitam esse sinal; chame Capability.execute() diretamente quando o cancelamento precisar alcançar a execução.
Rastreie tentativas e IDs de requisição
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 });
},
},
});Os hooks executam uma vez por tentativa HTTP, portanto entradas repetidas podem representar repetições automáticas. onRequest não contém o corpo. onResponse recebe uma cópia do corpo da resposta com dados removidos por nomes fixos. Mantenha os callbacks síncronos, sem lançamentos e sem trabalho ilimitado.
Armazene em cache um schema não secreto do catálogo
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 é independente; o cliente não o usa automaticamente. Falhas simultâneas de cache não são consolidadas, entradas expiradas são removidas na leitura e computações rejeitadas não são armazenadas. Nunca armazene valores de credenciais, tokens bearer ou decisões de autorização nesse cache.
