deepseek_user_id_with_session_name
· 3.9 KiB · Text
Brut
/**
* DeepSeek `user_id` — ONE ID PER SESSION, never one shared value.
*
* DeepSeek's Chat Completions API accepts a custom `user_id` body parameter
* (https://api-docs.deepseek.com/quick_start/rate_limit) used for:
* - content-safety review isolation
* - KV-cache isolation
* - scheduling/concurrency isolation
*
* WHY PER SESSION. Those are ISOLATION keys: a single fixed value puts every session that uses it
* into the SAME namespace, which is the opposite of what the parameter is for — three concurrent
* sessions then share whatever is keyed by it. The id is `<prefix>-<session name>` (e.g.
* `santiago-BUILDING-ATOMIQUE-101`), so the value both ISOLATES the session and NAMES it: the
* provider's own usage and rate-limit views show which session spent what, which a constant can
* never do.
*
* THE NAME COMES FROM pi.getSessionName() — the same accessor pi-intercom resolves a session's
* alias through — and the runtime session id is used when a session has no name yet, so the id is
* unique from the first request rather than only after someone renames the session. A rename
* changes the id and therefore starts a fresh cache namespace: correct, and the price of the id
* following the name.
*
* Pi's models.json config surface only supports HTTP `headers`, not body parameters, so this hooks
* the `before_provider_request` event, which can replace the outgoing payload. Scoped to
* `deepseek*` model ids so other providers are never touched.
*/
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
const USER_PREFIX = "santiago";
/** The id is a provider-facing token: DeepSeek's Chat Completions docs allow exactly
* [a-zA-Z0-9\-_], up to 512 characters (stated in the `user_id` row of the API reference), and ask
* that no user privacy information ride it. A session label is a workspace name, not personal data;
* the cap is far below their limit so a long label cannot be rejected on length. */
const MAX_ID = 96;
/**
* Does a session name look like an ALIAS (the thing this id wants) or like a PROMPT?
*
* An unaliased session's name is its opening prompt — this session's own first `session_info` record
* was the whole handoff text — and DeepSeek's API reference says plainly: "Do not include user
* privacy information in the user_id." So a prompt-shaped name is refused deliberately and the
* runtime session id is used instead; only a short, single-token label becomes the readable half.
*/
function looksLikeAlias(name: string): boolean {
return name !== "" && name.length <= 48 && !/\s/.test(name);
}
/**
* Build the per-session `user_id`. Exported so it can be asserted directly.
*
* NO FALLBACK TO A SHARED VALUE: if neither an alias nor a session id can be read, this returns
* undefined and the caller leaves the payload alone, because the one thing this id must never be
* is the SAME for two sessions.
*/
export function deepseekUserId(pi: Pick<ExtensionAPI, "getSessionName">): string | undefined {
const name = pi.getSessionName()?.trim();
const sessionId = process.env.PI_SESSION_ID?.trim();
const alias = name !== undefined && looksLikeAlias(name) ? name : undefined;
const basis = alias ?? (sessionId !== undefined && sessionId !== "" ? sessionId : undefined);
if (basis === undefined) {
return undefined;
}
const clean = basis.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_ID);
return clean === "" ? undefined : `${USER_PREFIX}-${clean}`;
}
export default function (pi: ExtensionAPI) {
pi.on("before_provider_request", (event) => {
const payload = event.payload as Record<string, unknown> | null | undefined;
if (!payload || typeof payload !== "object") {
return;
}
const model = typeof payload.model === "string" ? payload.model : "";
if (!model.startsWith("deepseek")) {
return;
}
const userId = deepseekUserId(pi);
if (userId === undefined) {
return;
}
return { ...payload, user_id: userId };
});
}
| 1 | /** |
| 2 | * DeepSeek `user_id` — ONE ID PER SESSION, never one shared value. |
| 3 | * |
| 4 | * DeepSeek's Chat Completions API accepts a custom `user_id` body parameter |
| 5 | * (https://api-docs.deepseek.com/quick_start/rate_limit) used for: |
| 6 | * - content-safety review isolation |
| 7 | * - KV-cache isolation |
| 8 | * - scheduling/concurrency isolation |
| 9 | * |
| 10 | * WHY PER SESSION. Those are ISOLATION keys: a single fixed value puts every session that uses it |
| 11 | * into the SAME namespace, which is the opposite of what the parameter is for — three concurrent |
| 12 | * sessions then share whatever is keyed by it. The id is `<prefix>-<session name>` (e.g. |
| 13 | * `santiago-BUILDING-ATOMIQUE-101`), so the value both ISOLATES the session and NAMES it: the |
| 14 | * provider's own usage and rate-limit views show which session spent what, which a constant can |
| 15 | * never do. |
| 16 | * |
| 17 | * THE NAME COMES FROM pi.getSessionName() — the same accessor pi-intercom resolves a session's |
| 18 | * alias through — and the runtime session id is used when a session has no name yet, so the id is |
| 19 | * unique from the first request rather than only after someone renames the session. A rename |
| 20 | * changes the id and therefore starts a fresh cache namespace: correct, and the price of the id |
| 21 | * following the name. |
| 22 | * |
| 23 | * Pi's models.json config surface only supports HTTP `headers`, not body parameters, so this hooks |
| 24 | * the `before_provider_request` event, which can replace the outgoing payload. Scoped to |
| 25 | * `deepseek*` model ids so other providers are never touched. |
| 26 | */ |
| 27 | |
| 28 | import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; |
| 29 | |
| 30 | const USER_PREFIX = "santiago"; |
| 31 | |
| 32 | /** The id is a provider-facing token: DeepSeek's Chat Completions docs allow exactly |
| 33 | * [a-zA-Z0-9\-_], up to 512 characters (stated in the `user_id` row of the API reference), and ask |
| 34 | * that no user privacy information ride it. A session label is a workspace name, not personal data; |
| 35 | * the cap is far below their limit so a long label cannot be rejected on length. */ |
| 36 | const MAX_ID = 96; |
| 37 | |
| 38 | /** |
| 39 | * Does a session name look like an ALIAS (the thing this id wants) or like a PROMPT? |
| 40 | * |
| 41 | * An unaliased session's name is its opening prompt — this session's own first `session_info` record |
| 42 | * was the whole handoff text — and DeepSeek's API reference says plainly: "Do not include user |
| 43 | * privacy information in the user_id." So a prompt-shaped name is refused deliberately and the |
| 44 | * runtime session id is used instead; only a short, single-token label becomes the readable half. |
| 45 | */ |
| 46 | function looksLikeAlias(name: string): boolean { |
| 47 | return name !== "" && name.length <= 48 && !/\s/.test(name); |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Build the per-session `user_id`. Exported so it can be asserted directly. |
| 52 | * |
| 53 | * NO FALLBACK TO A SHARED VALUE: if neither an alias nor a session id can be read, this returns |
| 54 | * undefined and the caller leaves the payload alone, because the one thing this id must never be |
| 55 | * is the SAME for two sessions. |
| 56 | */ |
| 57 | export function deepseekUserId(pi: Pick<ExtensionAPI, "getSessionName">): string | undefined { |
| 58 | const name = pi.getSessionName()?.trim(); |
| 59 | const sessionId = process.env.PI_SESSION_ID?.trim(); |
| 60 | const alias = name !== undefined && looksLikeAlias(name) ? name : undefined; |
| 61 | const basis = alias ?? (sessionId !== undefined && sessionId !== "" ? sessionId : undefined); |
| 62 | if (basis === undefined) { |
| 63 | return undefined; |
| 64 | } |
| 65 | const clean = basis.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_ID); |
| 66 | return clean === "" ? undefined : `${USER_PREFIX}-${clean}`; |
| 67 | } |
| 68 | |
| 69 | export default function (pi: ExtensionAPI) { |
| 70 | pi.on("before_provider_request", (event) => { |
| 71 | const payload = event.payload as Record<string, unknown> | null | undefined; |
| 72 | if (!payload || typeof payload !== "object") { |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | const model = typeof payload.model === "string" ? payload.model : ""; |
| 77 | if (!model.startsWith("deepseek")) { |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | const userId = deepseekUserId(pi); |
| 82 | if (userId === undefined) { |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | return { ...payload, user_id: userId }; |
| 87 | }); |
| 88 | } |