Two structural fixes for demo concurrency (3 single-slot suppliers thrashed
under Open WebUI's ~3 conversations per message):
Multi-slot suppliers (MAX_CHAT_SESSIONS, default 1):
- SupplierState is now capacity-aware: a Set of active escrow refs with
free-until-full status semantics, ref-scoped release(ref) (also fixes the
latent bug where handler catch-alls released another request's slot), and
a walletMutex serializing every wallet-spending chain op — Claim, Submit,
and wallet-health consolidation queue instead of racing the single
working/collateral UTxO. /status gains active_sessions/max_sessions.
Entry-ticket settle mode (CHAT_SETTLE_MODE=ticket, default full, both sides):
- The buyer's escrow post at session open is the ONLY chain op. The supplier
verifies the Open escrow as an entry ticket but never Claims/Submits
(/v1/chat/start → {status:"ticket"}; /v1/chat/end → receipt-less
{status:"ended"}); no hard-cap timer, so sessions may outlive deliver_by.
Used tickets can't reopen sessions (records retained; 409 session_exists).
- Buyer SDK: startChat/endChat are settle-mode aware, driven by the
supplier's response — mixed-mode rollout windows settle correctly.
- Gateway: ticket closes bill cost 0; the sweeper Reclaims Open tickets
after deliver_by WITHOUT closing live sessions (the conversation and its
affinity mirror survive their reclaimed ticket), gates reclaims on
deliver_by, and keeps an in-process settled-refs memory so terminal
indexer rows aren't retried every sweep forever.
Deployment (kimi-chat trio + gateway): ticket mode, 8 slots per supplier,
idle policy gateway 10 min / supplier 15 min (fixes the inversion where the
supplier idle-closed before the gateway janitor).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
/**
|
|
* Supplier-side sample config fixture — M1-C RED phase
|
|
*
|
|
* Provides a deterministic SupplierConfig for test injection.
|
|
* Built INDEPENDENTLY from buyer-side fixtures. No shared helpers.
|
|
*/
|
|
|
|
import type { SupplierConfig } from "../../../supplier/src/config.js";
|
|
import { SUPPLIER_PKH, SUPPLIER_PRIVATE_KEY_HEX } from "./wallet-keys.js";
|
|
|
|
/** The advert UTxO that this supplier's config points at. */
|
|
export const SAMPLE_ADVERT_TX_HASH = "b".repeat(64);
|
|
export const SAMPLE_ADVERT_INDEX = 0;
|
|
|
|
/** A deterministic valid 64-char hex tx hash for use in escrow refs. */
|
|
export const SAMPLE_ESCROW_TX_HASH = "f".repeat(64);
|
|
|
|
/** Full supplier config used in route handler tests. */
|
|
export function buildSampleConfig(): SupplierConfig {
|
|
return {
|
|
supplierPrivKeyHex: SUPPLIER_PRIVATE_KEY_HEX,
|
|
ogmiosUrl: "ws://localhost:1337",
|
|
ollamaUrl: "http://localhost:11434",
|
|
advertRef: {
|
|
txHash: SAMPLE_ADVERT_TX_HASH,
|
|
index: SAMPLE_ADVERT_INDEX,
|
|
},
|
|
networkId: 0,
|
|
port: 8080,
|
|
ollamaTimeoutMs: 120_000,
|
|
piperUrl: "",
|
|
piperTimeoutMs: 120_000,
|
|
capabilityKind: "chat",
|
|
maxChatSessions: 1,
|
|
chatSettleMode: "full",
|
|
liveChain: false,
|
|
};
|
|
}
|
|
|
|
/** TTS variant: same wallet/chain plumbing, capabilityKind="tts" + Piper URL set. */
|
|
export function buildSampleTtsConfig(): SupplierConfig {
|
|
return {
|
|
...buildSampleConfig(),
|
|
ollamaUrl: "",
|
|
piperUrl: "http://piper.fake",
|
|
capabilityKind: "tts",
|
|
};
|
|
}
|
|
|
|
/** Env map matching the sample config above, for loadConfig() unit tests. */
|
|
export function buildSampleEnv(): Record<string, string> {
|
|
return {
|
|
SUPPLIER_PRIV_KEY_HEX: SUPPLIER_PRIVATE_KEY_HEX,
|
|
OGMIOS_URL: "ws://localhost:1337",
|
|
OLLAMA_URL: "http://localhost:11434",
|
|
ADVERT_REF: `${SAMPLE_ADVERT_TX_HASH}#${SAMPLE_ADVERT_INDEX}`,
|
|
NETWORK_ID: "0",
|
|
PORT: "8080",
|
|
OLLAMA_TIMEOUT_MS: "120000",
|
|
};
|
|
}
|
|
|
|
/** The pkh embedded in the sample config. */
|
|
export { SUPPLIER_PKH };
|