Files
ozan a81a450e7e feat: monorepo consolidation — merge CLI, bot, admin, team-tool, website, docs, runner, proxy
Merged into tinqs/studio:
- cmd/tinqs-cli/    — tinqs-cli (Go binary, from bot/cli)
- cmd/tea/          — Gitea CLI tool (from tinqs/cli-tea)
- services/bot/     — Bot service (from tinqs-ltd/bot on git.arikigame.com)
- services/admin/   — Admin panel (from tinqs/admin)
- services/team-tool/ — Team Tool (from tinqs/team-tool)
- services/proxy/   — tinqs-proxy (from bot/proxy)
- web/landing/      — tinqs.com website (from tinqs/website)
- web/docs/         — Platform docs (from tinqs/docs)
- web/blog/         — Blog (placeholder)
- runner/           — Ephemeral CI runner (from tinqs/runner)

All source repos will be deleted after verification.
2026-05-22 04:55:50 +00:00

60 lines
2.0 KiB
TypeScript

/**
* Reasoning-content cache for the DeepSeek inference proxy.
*
* DeepSeek's thinking-mode tool-call API requires that `reasoning_content`
* from the assistant's previous turn be sent back on every continuation:
* https://api-docs.deepseek.com/guides/thinking_mode#tool-calls
*
* Cursor (and a latent path in Tinqs IDE) strip that field when replaying
* history. This module caches reasoning_content as it streams in from
* DeepSeek, keyed by signatures of the assistant message + its tool_calls,
* so the request transform can inject it back when the client omits it.
*
* Pure key-derivation helpers live in lib/reasoning-keys.ts (testable
* without Redis). This file wraps them with ioredis-backed storage.
*/
import { redis } from "./db";
import { type AssistantMessage, reasoningKeys } from "./reasoning-keys";
export {
type AssistantMessage,
type ToolCall,
type ToolCallFn,
messageSignature,
normalizeToolCall,
reasoningKeys,
toolCallSignature,
userScope,
} from "./reasoning-keys";
const DEFAULT_TTL_SECONDS = 30 * 24 * 60 * 60; // 30 days, matches Python default
/** Look up cached reasoning_content for an assistant message in this scope. */
export async function lookupReasoning(
scope: string,
msg: AssistantMessage,
): Promise<string | null> {
if (!redis) return null;
for (const key of reasoningKeys(scope, msg)) {
const v = await redis.get(key);
if (typeof v === "string" && v.length > 0) return v;
}
return null;
}
/** Cache reasoning_content under every alias key for this assistant message. */
export async function storeReasoning(
scope: string,
msg: AssistantMessage,
reasoning: string,
ttlSeconds = DEFAULT_TTL_SECONDS,
): Promise<number> {
if (!redis) return 0;
if (typeof reasoning !== "string" || reasoning.length === 0) return 0;
const keys = reasoningKeys(scope, msg);
const pipe = redis.pipeline();
for (const k of keys) pipe.set(k, reasoning, "EX", ttlSeconds);
await pipe.exec();
return keys.length;
}