Files
agents-marketplace/gateway/src/openai/models.ts
david 4ebe7c9683 feat(gateway,supplier,shared): shared demo key with session-backed OpenAI endpoint + tool-calling
MVP demo: one operator-funded API key anyone can plug into an IDE/harness,
running real escrow-per-session marketplace conversations (llm.chat.v1)
behind the standard OpenAI /chat/completions wire shape.

- shared: ChatMessage gains tool role + tool_calls; normalizeChatMessage/
  chatMessagesEquivalent keep supplier transcript and gateway mirror
  hash-identical for receipts (field presence is part of the identity)
- supplier: /v1/chat/message accepts {messages,tools,tool_choice} alongside
  legacy {content}; callOpenAiStream forwards tools, accumulates fragmented
  delta.tool_calls, allows content-less tool turns; done frame carries the
  assistant message verbatim + finish_reason + usage
- gateway: demo flag on api_keys (additive migration), create-demo-key CLI,
  withdraw 403 + hidden deposit address for demo keys, per-IP demo rate
  limit (demo keys skip the shared per-key limit), models filtered to chat
  capability
- gateway executor (demoChat.ts): transcript-prefix affinity maps stateless
  calls onto open sessions, forwards only unseen deltas, transparently
  reopens on supplier idle-close; never falls back to one-shot (its settle
  path assumes <=1 in-flight escrow per wallet)
- mutex scope: key mutex held only for chain work (open/close); off-chain
  turns serialize on per-session locks so replicas serve users concurrently
- sweeper: chat-aware reclaim guard (35min vs 11min) and abandoned-session
  cleanup; terminal state confirmed via on-chain queryUtxo (indexer keeps
  lineage rows at their old state forever)
- deploy: kimi-chat replicas 2+3 compose files; chat idle timeout 2.5min

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:30:17 +02:00

27 lines
998 B
TypeScript

/**
* gateway/src/openai/models.ts — GET /openai/v1/models.
*
* Distinct models advertised by Active suppliers, in the OpenAI list shape.
*/
import type { Request, Response } from "express";
import type { GatewayDeps } from "../deps.js";
import { asyncHandler } from "../middleware/http.js";
import { requireKey } from "../middleware/apiKeyAuth.js";
import { listModels } from "../routing/selectSupplier.js";
import { buildModelsList } from "./shapes.js";
import { CAPABILITY as CHAT_CAPABILITY } from "./sessions.js";
export function makeModelsHandler(deps: GatewayDeps) {
return asyncHandler(async (req: Request, res: Response) => {
const keyRow = requireKey(req);
// Demo keys only reach chat-capable suppliers (session-backed executor).
const models = await listModels({
indexerUrl: deps.config.indexerUrl,
capabilityId: keyRow.demo ? CHAT_CAPABILITY : undefined,
fetchFn: deps.fetchFn,
});
res.status(200).json(buildModelsList(models));
});
}