@volter/twin-openai 0.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -30
- package/defaults/handlers.json +10 -0
- package/dist/defaults/handlers.json +10 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +29 -0
- package/dist/src/generated/surface.gen.json +1 -0
- package/dist/src/generated/ui.gen.json +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +72 -0
- package/dist/src/manifest.d.ts +6 -0
- package/dist/src/manifest.js +323 -0
- package/dist/src/openai-budget.d.ts +53 -0
- package/dist/src/openai-budget.js +147 -0
- package/dist/src/openai-capabilities.d.ts +4 -0
- package/dist/src/openai-capabilities.js +1569 -0
- package/dist/src/openai-conformance.d.ts +13 -0
- package/dist/src/openai-conformance.js +116 -0
- package/dist/src/openai-connector.d.ts +86 -0
- package/dist/src/openai-connector.js +291 -0
- package/dist/src/openai-media.d.ts +43 -0
- package/dist/src/openai-media.js +257 -0
- package/dist/src/openai-models.d.ts +74 -0
- package/dist/src/openai-models.js +148 -0
- package/dist/src/openai-scenario.d.ts +51 -0
- package/dist/src/openai-scenario.js +166 -0
- package/dist/src/openai-server.d.ts +40 -0
- package/dist/src/openai-server.js +126 -0
- package/dist/src/openai-stub.d.ts +82 -0
- package/dist/src/openai-stub.js +256 -0
- package/dist/src/openai-twin.d.ts +182 -0
- package/dist/src/openai-twin.js +1117 -0
- package/dist/src/openai-types.d.ts +194 -0
- package/dist/src/openai-types.js +4 -0
- package/dist/src/openai-webhooks.d.ts +47 -0
- package/dist/src/openai-webhooks.js +99 -0
- package/dist/src/screens/api-keys.d.ts +16 -0
- package/dist/src/screens/api-keys.js +131 -0
- package/dist/src/screens/session.d.ts +22 -0
- package/dist/src/screens/session.js +115 -0
- package/dist/src/semantics/assistants.d.ts +2 -0
- package/dist/src/semantics/assistants.js +331 -0
- package/dist/src/semantics/audio.d.ts +2 -0
- package/dist/src/semantics/audio.js +27 -0
- package/dist/src/semantics/batches.d.ts +4 -0
- package/dist/src/semantics/batches.js +86 -0
- package/dist/src/semantics/chat-completions.d.ts +3 -0
- package/dist/src/semantics/chat-completions.js +58 -0
- package/dist/src/semantics/containers.d.ts +2 -0
- package/dist/src/semantics/containers.js +147 -0
- package/dist/src/semantics/embeddings.d.ts +2 -0
- package/dist/src/semantics/embeddings.js +13 -0
- package/dist/src/semantics/evals.d.ts +2 -0
- package/dist/src/semantics/evals.js +173 -0
- package/dist/src/semantics/files.d.ts +13 -0
- package/dist/src/semantics/files.js +59 -0
- package/dist/src/semantics/fine-tuning.d.ts +4 -0
- package/dist/src/semantics/fine-tuning.js +178 -0
- package/dist/src/semantics/images.d.ts +2 -0
- package/dist/src/semantics/images.js +18 -0
- package/dist/src/semantics/index.d.ts +8 -0
- package/dist/src/semantics/index.js +46 -0
- package/dist/src/semantics/models.d.ts +2 -0
- package/dist/src/semantics/models.js +34 -0
- package/dist/src/semantics/moderations.d.ts +2 -0
- package/dist/src/semantics/moderations.js +12 -0
- package/dist/src/semantics/organization.d.ts +2 -0
- package/dist/src/semantics/organization.js +67 -0
- package/dist/src/semantics/progress.d.ts +22 -0
- package/dist/src/semantics/progress.js +63 -0
- package/dist/src/semantics/responses.d.ts +3 -0
- package/dist/src/semantics/responses.js +153 -0
- package/dist/src/semantics/shared.d.ts +32 -0
- package/dist/src/semantics/shared.js +69 -0
- package/dist/src/semantics/uploads.d.ts +2 -0
- package/dist/src/semantics/uploads.js +84 -0
- package/dist/src/semantics/vector-stores.d.ts +2 -0
- package/dist/src/semantics/vector-stores.js +281 -0
- package/dist/test-fixtures/openai-openapi-operations.SOURCE.md +18 -0
- package/dist/test-fixtures/openai-openapi-operations.json +1849 -0
- package/package.json +21 -10
- package/src/cli.ts +9 -7
- package/src/generated/surface.gen.json +1 -0
- package/src/generated/ui.gen.json +1 -0
- package/src/index.ts +20 -10
- package/src/manifest.ts +343 -0
- package/src/openai-budget.ts +4 -4
- package/src/openai-capabilities.ts +177 -195
- package/src/openai-conformance.ts +1 -1
- package/src/openai-connector.ts +40 -43
- package/src/openai-media.ts +225 -0
- package/src/openai-models.ts +145 -15
- package/src/openai-scenario.ts +46 -10
- package/src/openai-server.ts +65 -108
- package/src/openai-stub.ts +54 -30
- package/src/openai-twin.ts +760 -1665
- package/src/openai-types.ts +24 -6
- package/src/openai-webhooks.ts +2 -1
- package/src/screens/api-keys.tsx +138 -0
- package/src/screens/session.tsx +131 -0
- package/src/semantics/assistants.ts +336 -0
- package/src/semantics/audio.ts +31 -0
- package/src/semantics/batches.ts +88 -0
- package/src/semantics/chat-completions.ts +66 -0
- package/src/semantics/containers.ts +151 -0
- package/src/semantics/embeddings.ts +19 -0
- package/src/semantics/evals.ts +182 -0
- package/src/semantics/files.ts +67 -0
- package/src/semantics/fine-tuning.ts +185 -0
- package/src/semantics/images.ts +23 -0
- package/src/semantics/index.ts +52 -0
- package/src/semantics/models.ts +41 -0
- package/src/semantics/moderations.ts +14 -0
- package/src/semantics/organization.ts +76 -0
- package/src/semantics/progress.ts +72 -0
- package/src/semantics/responses.ts +151 -0
- package/src/semantics/shared.ts +82 -0
- package/src/semantics/uploads.ts +92 -0
- package/src/semantics/vector-stores.ts +279 -0
- package/test-fixtures/openai-openapi-operations.SOURCE.md +4 -5
- package/test-fixtures/openai-openapi-operations.json +224 -1334
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"generatedBy":"scripts/derive-pack.ts","resources":{"Model":{"list":"/v1/models","retrieve":"/v1/models/{model}","actions":[],"states":{},"embeds":{}},"OpenAIFile":{"list":"/v1/files","retrieve":"/v1/files/{file_id}","create":{"path":"/v1/files","encoding":"multipart","body":[{"name":"expires_after","type":"FileExpirationAfter","required":false},{"name":"file","type":"string","required":true},{"name":"purpose","type":"string","required":true}]},"actions":[],"states":{},"embeds":{}},"FineTuningJob":{"list":"/v1/fine_tuning/jobs","retrieve":"/v1/fine_tuning/jobs/{fine_tuning_job_id}","create":{"path":"/v1/fine_tuning/jobs","encoding":"json","body":[{"name":"hyperparameters","type":"object","required":false},{"name":"integrations","type":"array","required":false},{"name":"metadata","type":"Metadata","required":false},{"name":"method","type":"FineTuneMethod","required":false},{"name":"model","type":"union","required":true},{"name":"seed","type":"integer","required":false},{"name":"suffix","type":"string","required":false},{"name":"training_file","type":"string","required":true},{"name":"validation_file","type":"string","required":false}]},"actions":[{"operation":"cancelFineTuningJob","method":"POST","path":"/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel","field":"status","from":["validating_files","queued","running","paused"],"to":"cancelled"},{"operation":"pauseFineTuningJob","method":"POST","path":"/v1/fine_tuning/jobs/{fine_tuning_job_id}/pause","field":"status","from":["validating_files","queued","running"],"to":"paused"},{"operation":"resumeFineTuningJob","method":"POST","path":"/v1/fine_tuning/jobs/{fine_tuning_job_id}/resume","field":"status","from":["paused"],"to":"queued"}],"states":{"status":["cancelled","paused","queued","running","succeeded","validating_files"]},"embeds":{}},"FineTuningJobEvent":{"list":"/v1/fine_tuning/jobs/{fine_tuning_job_id}/events","actions":[],"states":{},"embeds":{}},"FineTuningJobCheckpoint":{"list":"/v1/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints","actions":[],"states":{},"embeds":{}},"VectorStoreObject":{"list":"/v1/vector_stores","retrieve":"/v1/vector_stores/{vector_store_id}","create":{"path":"/v1/vector_stores","encoding":"json","body":[{"name":"chunking_strategy","type":"union","required":false},{"name":"description","type":"string","required":false},{"name":"expires_after","type":"VectorStoreExpirationAfter","required":false},{"name":"file_ids","type":"array","required":false},{"name":"metadata","type":"Metadata","required":false},{"name":"name","type":"string","required":false}]},"actions":[],"states":{"status":["completed"]},"embeds":{}},"VectorStoreFileObject":{"list":"/v1/vector_stores/{vector_store_id}/files","retrieve":"/v1/vector_stores/{vector_store_id}/files/{file_id}","create":{"path":"/v1/vector_stores/{vector_store_id}/files","encoding":"json","body":[{"name":"attributes","type":"VectorStoreFileAttributes","required":false},{"name":"chunking_strategy","type":"ChunkingStrategyRequestParam","required":false},{"name":"file_id","type":"string","required":true}]},"actions":[],"states":{"status":["cancelled","completed","in_progress"]},"embeds":{}},"VectorStoreFileBatchObject":{"retrieve":"/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}","create":{"path":"/v1/vector_stores/{vector_store_id}/file_batches","encoding":"json","body":[{"name":"attributes","type":"VectorStoreFileAttributes","required":false},{"name":"chunking_strategy","type":"ChunkingStrategyRequestParam","required":false},{"name":"file_ids","type":"array","required":false},{"name":"files","type":"array","required":false}]},"actions":[{"operation":"cancelVectorStoreFileBatch","method":"POST","path":"/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel","field":"status","from":["in_progress"],"to":"cancelled"}],"states":{"status":["cancelled","completed","in_progress"]},"embeds":{}},"CreateChatCompletionResponse":{"list":"/v1/chat/completions","retrieve":"/v1/chat/completions/{completion_id}","create":{"path":"/v1/chat/completions","encoding":"json","body":[]},"actions":[],"states":{},"embeds":{}},"Response":{"retrieve":"/v1/responses/{response_id}","create":{"path":"/v1/responses","encoding":"json","body":[]},"actions":[{"operation":"cancelResponse","method":"POST","path":"/v1/responses/{response_id}/cancel","field":"status","from":["queued","in_progress"],"to":"cancelled"},{"operation":"cancelResponse","method":"POST","path":"/v1/responses/{response_id}/cancel","field":"status","from":["cancelled"]}],"states":{"status":["cancelled","completed","in_progress","queued"]},"embeds":{}},"BetaResponse":{"retrieve":"/v1/responses/{response_id}","create":{"path":"/v1/responses","encoding":"json","body":[]},"actions":[{"operation":"beta_cancelResponse","method":"POST","path":"/v1/responses/{response_id}/cancel","field":"status","from":["queued","in_progress"],"to":"cancelled"},{"operation":"beta_cancelResponse","method":"POST","path":"/v1/responses/{response_id}/cancel","field":"status","from":["cancelled"]}],"states":{"status":["cancelled","completed","in_progress","queued"]},"embeds":{}},"ItemResource":{"list":"/v1/responses/{response_id}/input_items","actions":[],"states":{},"embeds":{}},"BetaItemResource":{"list":"/v1/responses/{response_id}/input_items","actions":[],"states":{},"embeds":{}},"AssistantObject":{"list":"/v1/assistants","retrieve":"/v1/assistants/{assistant_id}","create":{"path":"/v1/assistants","encoding":"json","body":[{"name":"description","type":"union","required":false},{"name":"instructions","type":"union","required":false},{"name":"metadata","type":"Metadata","required":false},{"name":"model","type":"union","required":true},{"name":"name","type":"union","required":false},{"name":"reasoning_effort","type":"ReasoningEffort","required":false},{"name":"response_format","type":"union","required":false},{"name":"temperature","type":"union","required":false},{"name":"tool_resources","type":"union","required":false},{"name":"tools","type":"array","required":false},{"name":"top_p","type":"union","required":false}]},"actions":[],"states":{},"embeds":{}},"ThreadObject":{"retrieve":"/v1/threads/{thread_id}","create":{"path":"/v1/threads","encoding":"json","body":[{"name":"messages","type":"array","required":false},{"name":"metadata","type":"Metadata","required":false},{"name":"tool_resources","type":"union","required":false}]},"actions":[],"states":{},"embeds":{}},"MessageObject":{"list":"/v1/threads/{thread_id}/messages","retrieve":"/v1/threads/{thread_id}/messages/{message_id}","create":{"path":"/v1/threads/{thread_id}/messages","encoding":"json","body":[{"name":"attachments","type":"union","required":false},{"name":"content","type":"union","required":true},{"name":"metadata","type":"Metadata","required":false},{"name":"role","type":"string","required":true}]},"actions":[],"states":{},"embeds":{}},"RunObject":{"list":"/v1/threads/{thread_id}/runs","retrieve":"/v1/threads/{thread_id}/runs/{run_id}","create":{"path":"/v1/threads/{thread_id}/runs","encoding":"json","body":[{"name":"additional_instructions","type":"string","required":false},{"name":"additional_messages","type":"array","required":false},{"name":"assistant_id","type":"string","required":true},{"name":"instructions","type":"string","required":false},{"name":"max_completion_tokens","type":"integer","required":false},{"name":"max_prompt_tokens","type":"integer","required":false},{"name":"metadata","type":"Metadata","required":false},{"name":"model","type":"union","required":false},{"name":"parallel_tool_calls","type":"ParallelToolCalls","required":false},{"name":"reasoning_effort","type":"ReasoningEffort","required":false},{"name":"response_format","type":"AssistantsApiResponseFormatOption","required":false},{"name":"stream","type":"boolean","required":false},{"name":"temperature","type":"number","required":false},{"name":"tool_choice","type":"any","required":false},{"name":"tools","type":"array","required":false},{"name":"top_p","type":"number","required":false},{"name":"truncation_strategy","type":"any","required":false}]},"actions":[{"operation":"cancelRun","method":"POST","path":"/v1/threads/{thread_id}/runs/{run_id}/cancel","field":"status","from":["queued","in_progress","requires_action"],"to":"cancelling"},{"operation":"submitToolOuputsToRun","method":"POST","path":"/v1/threads/{thread_id}/runs/{run_id}/submit_tool_outputs","field":"status","from":["requires_action"],"to":"queued"}],"states":{"status":["cancelled","cancelling","completed","in_progress","queued","requires_action"]},"embeds":{}},"RunStepObject":{"list":"/v1/threads/{thread_id}/runs/{run_id}/steps","retrieve":"/v1/threads/{thread_id}/runs/{run_id}/steps/{step_id}","actions":[],"states":{},"embeds":{}},"Project":{"list":"/v1/organization/projects","retrieve":"/v1/organization/projects/{project_id}","create":{"path":"/v1/organization/projects","encoding":"json","body":[{"name":"external_key_id","type":"union","required":false},{"name":"geography","type":"union","required":false},{"name":"name","type":"string","required":true},{"name":"residency","type":"union","required":false}]},"actions":[{"operation":"archive-project","method":"POST","path":"/v1/organization/projects/{project_id}/archive","field":"status","from":"*","to":"archived"}],"states":{"status":["active","archived"]},"embeds":{}},"ProjectApiKey":{"list":"/v1/organization/projects/{project_id}/api_keys","retrieve":"/v1/organization/projects/{project_id}/api_keys/{api_key_id}","actions":[],"states":{},"embeds":{}},"UsageTimeBucket":{"list":"/v1/organization/costs","actions":[],"states":{},"embeds":{}},"Eval":{"list":"/v1/evals","retrieve":"/v1/evals/{eval_id}","create":{"path":"/v1/evals","encoding":"json","body":[{"name":"data_source_config","type":"union","required":true},{"name":"metadata","type":"Metadata","required":false},{"name":"name","type":"string","required":false},{"name":"testing_criteria","type":"array","required":true}]},"actions":[],"states":{},"embeds":{}},"EvalRun":{"list":"/v1/evals/{eval_id}/runs","retrieve":"/v1/evals/{eval_id}/runs/{run_id}","create":{"path":"/v1/evals/{eval_id}/runs","encoding":"json","body":[{"name":"data_source","type":"union","required":true},{"name":"metadata","type":"Metadata","required":false},{"name":"name","type":"string","required":false}]},"actions":[{"operation":"cancelEvalRun","method":"POST","path":"/v1/evals/{eval_id}/runs/{run_id}","field":"status","from":["queued","in_progress"],"to":"canceled"}],"states":{"status":["canceled","completed","in_progress","queued"]},"embeds":{}},"EvalRunOutputItem":{"list":"/v1/evals/{eval_id}/runs/{run_id}/output_items","actions":[],"states":{},"embeds":{}},"ContainerResource":{"list":"/v1/containers","retrieve":"/v1/containers/{container_id}","create":{"path":"/v1/containers","encoding":"json","body":[{"name":"expires_after","type":"object","required":false},{"name":"file_ids","type":"array","required":false},{"name":"memory_limit","type":"string","required":false},{"name":"name","type":"string","required":true},{"name":"network_policy","type":"union","required":false},{"name":"skills","type":"array","required":false}]},"actions":[],"states":{"status":["expired","running"]},"embeds":{}},"ContainerFileResource":{"list":"/v1/containers/{container_id}/files","retrieve":"/v1/containers/{container_id}/files/{file_id}","create":{"path":"/v1/containers/{container_id}/files","encoding":"json","body":[{"name":"file","type":"string","required":false},{"name":"file_id","type":"string","required":false}]},"actions":[],"states":{},"embeds":{}},"Batch":{"list":"/v1/batches","retrieve":"/v1/batches/{batch_id}","create":{"path":"/v1/batches","encoding":"json","body":[{"name":"completion_window","type":"string","required":true},{"name":"endpoint","type":"string","required":true},{"name":"input_file_id","type":"string","required":true},{"name":"metadata","type":"Metadata","required":false},{"name":"output_expires_after","type":"BatchFileExpirationAfter","required":false}]},"actions":[{"operation":"cancelBatch","method":"POST","path":"/v1/batches/{batch_id}/cancel","field":"status","from":["validating","in_progress","finalizing"],"to":"cancelling"}],"states":{"status":["cancelled","cancelling","completed","finalizing","in_progress","validating"]},"embeds":{}},"Upload":{"actions":[],"states":{"status":["cancelled","completed","expired","pending"]},"embeds":{}}}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { handleOpenAITwinRequest, streamChat } from './openai-twin.js';
|
|
2
|
+
export type { OpenAIRequest, OpenAIResponseEnvelope } from './openai-twin.js';
|
|
3
|
+
export { createOpenAITwinFetch, createOpenAITwinServer } from './openai-server.js';
|
|
4
|
+
export type { OpenAITwinFetchOptions } from './openai-server.js';
|
|
5
|
+
export { OPENAI_MODELS, findModel } from './openai-models.js';
|
|
6
|
+
export type { OpenAIModel } from './openai-models.js';
|
|
7
|
+
export { countPromptTokens, estimateTokens, stubAssistantText, stubToolCall, contentToText, lastUserText, pseudoEmbedding, moderateText, MODERATION_CATEGORIES, } from './openai-stub.js';
|
|
8
|
+
export type { ModerationResult } from './openai-stub.js';
|
|
9
|
+
export type { ChatMessageParam, ChatToolCall, ChatCompletion, ChatChoice, ChatUsage, OpenAIResponse, EmbeddingResponse, Embedding, OpenAIError, SseEvent, SseSink, } from './openai-types.js';
|
|
10
|
+
export { performOpenAIAction, openaiExecuteOver, openaiRequestForAction, liveOpenAIExecute, mapFile, mapBatch, mapFineTune, mapVectorStore, pullOpenAIState, pushOpenAIAction, syncOpenAIFromReal, } from './openai-connector.js';
|
|
11
|
+
export type { OpenAIExecute, LiveOpenAIOptions } from './openai-connector.js';
|
|
12
|
+
export { OPENAI_BUDGET_CEILING, OPENAI_BUDGET_MAX_RETRY_AFTER_S, OPENAI_BUDGET_WINDOW_MS, OPENAI_CALL_WEIGHTS, OPENAI_RATE_BUDGET, OpenAIBudget, OpenAIBudgetError, openaiBudgetPath, openaiCallWeight, } from './openai-budget.js';
|
|
13
|
+
export type { OpenAIBudgetErrorKind, OpenAIBudgetOptions, OpenAIBudgetReservation, OpenAIBudgetSnapshot } from './openai-budget.js';
|
|
14
|
+
export { verifyOpenAIWebhook, computeOpenAIWebhookSignature, buildSignedOpenAIWebhook, OpenAIWebhookVerificationError, OPENAI_WEBHOOK_EVENT_TYPES, } from './openai-webhooks.js';
|
|
15
|
+
export type { OpenAIWebhookEvent } from './openai-webhooks.js';
|
|
16
|
+
import type { TwinPack } from '@volter/world-core';
|
|
17
|
+
export declare const pack: TwinPack;
|
|
18
|
+
export { createOpenAIScenarioEngine, loadOpenAIScenarioDocument, openaiScenarioAdapter, realizeOpenAIRespond } from './openai-scenario.js';
|
|
19
|
+
export type { OpenAIScenarioEngine, OpenAIScenarioRequest, OpenAIScenarioRespond } from './openai-scenario.js';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @volter/twin-openai — the OpenAI twin (one vendor, one package), built on the shared
|
|
2
|
+
// @volter/world-core kernel. The OpenAI API: a vendor-faithful protocol envelope (chat
|
|
3
|
+
// completions/streaming/tool_calls, the Responses API, embeddings), a static models catalog,
|
|
4
|
+
// deterministic moderations, and STATEFUL files / batches / fine-tuning jobs / vector stores —
|
|
5
|
+
// over an event/action log. (OpenAI is an API-first vendor with no meaningful product UI worth
|
|
6
|
+
// mirroring — docs/contributing/architecture.md C1b — so this pack ships no mirror.)
|
|
7
|
+
//
|
|
8
|
+
// THE HONEST CARVE-OUTS: the twin cannot run the model, so POST /v1/chat/completions and
|
|
9
|
+
// /v1/responses return a DETERMINISTIC STUB completion (clearly labeled), and /v1/embeddings
|
|
10
|
+
// returns DETERMINISTIC pseudo-vectors — never pretending to be real inference. Everything
|
|
11
|
+
// around them — the wire protocol — is faithful. (Conformance + capability tooling live in
|
|
12
|
+
// @volter/world-tooling, a dev dependency — NOT re-exported here, per E2.)
|
|
13
|
+
export { handleOpenAITwinRequest, streamChat } from "./openai-twin.js";
|
|
14
|
+
export { createOpenAITwinFetch, createOpenAITwinServer } from "./openai-server.js";
|
|
15
|
+
export { OPENAI_MODELS, findModel } from "./openai-models.js";
|
|
16
|
+
export { countPromptTokens, estimateTokens, stubAssistantText, stubToolCall, contentToText, lastUserText, pseudoEmbedding, moderateText, MODERATION_CATEGORIES, } from "./openai-stub.js";
|
|
17
|
+
export { performOpenAIAction, openaiExecuteOver, openaiRequestForAction, liveOpenAIExecute, mapFile, mapBatch, mapFineTune, mapVectorStore, pullOpenAIState, pushOpenAIAction, syncOpenAIFromReal, } from "./openai-connector.js";
|
|
18
|
+
// The client-side rate budget — the fail-closed backstop `liveOpenAIExecute` routes every live
|
|
19
|
+
// request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
|
|
20
|
+
// here is OpenAI's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
|
|
21
|
+
// bindings. Exported so an operator can inspect spend (`snapshot`) and so a caller can catch
|
|
22
|
+
// `OpenAIBudgetError` by type; there is deliberately no export that disables the guard.
|
|
23
|
+
export { OPENAI_BUDGET_CEILING, OPENAI_BUDGET_MAX_RETRY_AFTER_S, OPENAI_BUDGET_WINDOW_MS, OPENAI_CALL_WEIGHTS, OPENAI_RATE_BUDGET, OpenAIBudget, OpenAIBudgetError, openaiBudgetPath, openaiCallWeight, } from "./openai-budget.js";
|
|
24
|
+
export { verifyOpenAIWebhook, computeOpenAIWebhookSignature, buildSignedOpenAIWebhook, OpenAIWebhookVerificationError, OPENAI_WEBHOOK_EVENT_TYPES, } from "./openai-webhooks.js";
|
|
25
|
+
import { registerPack } from '@volter/world-core';
|
|
26
|
+
import { OPENAI_RATE_BUDGET as RATE_BUDGET } from "./openai-budget.js";
|
|
27
|
+
import { performOpenAIAction } from "./openai-connector.js";
|
|
28
|
+
export const pack = {
|
|
29
|
+
vendor: 'openai',
|
|
30
|
+
// PROTOCOL 2 (docs/contributing/architecture.md#protocol-2-the-pack-is-a-plugin): the wire, the tree, and the real apply — a recorded call
|
|
31
|
+
// forwarded as the same request. A generative vendor has no state to refresh from.
|
|
32
|
+
protocol: '2',
|
|
33
|
+
stateSystem: { perform: performOpenAIAction },
|
|
34
|
+
roundTrip: { method: 'POST', path: '/v1/chat/completions', body: { model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'round trip' }] }, headers: { authorization: 'Bearer sk-round-trip' } },
|
|
35
|
+
// The SAME object openai-budget.ts declares at module load — one source of truth, so registering
|
|
36
|
+
// the pack and importing the connector can never arm two different ceilings.
|
|
37
|
+
rateBudget: RATE_BUDGET,
|
|
38
|
+
transport: 'rest',
|
|
39
|
+
archetype: 'generative',
|
|
40
|
+
bin: 'world-openai',
|
|
41
|
+
resources: ['file', 'batch', 'fine_tuning_job', 'vector_store', 'vector_store_file', 'response', 'chat_completion'],
|
|
42
|
+
specSource: 'OpenAI API (envelope-faithful; model output is a deterministic stub, embeddings are deterministic pseudo-vectors)',
|
|
43
|
+
description: 'OpenAI API twin — faithful protocol envelope (chat completions/streaming/tool_calls, Responses API, embeddings), models, moderations, stateful files/batches/fine-tuning/vector stores; generative output is a labeled stub.',
|
|
44
|
+
// Adoption + interception, all in the pack's one home (descriptor-first back-migration, adding-a-twin.md §3,
|
|
45
|
+
// 2026-08-31; the bare `openai` stem and both SDK names moved off the central maps unchanged).
|
|
46
|
+
// The stems are the bare OPENAI_* pair plus the credential-var shapes real apps use beside it
|
|
47
|
+
// (AP_OPENAI_*, OPENAI_MODERATION_*).
|
|
48
|
+
adoption: {
|
|
49
|
+
// OpenAI's official Python library and Agents SDK. THE DISTRIBUTION IS THE CLAIM: `openai` stays
|
|
50
|
+
// this pack's even when a caller points it at DeepSeek, xAI, OpenRouter, Perplexity, an AI Gateway
|
|
51
|
+
// or an Azure OpenAI endpoint - those packs claim the ENDPOINT ENV that routes it, never this name.
|
|
52
|
+
// `tiktoken` is deliberately UNCLAIMED: it is a local tokenizer that makes no API call at all.
|
|
53
|
+
pypi: ['openai', 'openai-agents'],
|
|
54
|
+
sdks: ['openai', '@ai-sdk/openai'],
|
|
55
|
+
// 'REWORKDPLATFORMOPENAI' — AgentGPT names its key REWORKD_PLATFORM_OPENAI_API_KEY, the same
|
|
56
|
+
// app-prefixed shape as APOPENAI (ladder classification 2026-09-02). Deliberately NOT
|
|
57
|
+
// 'OPENAIOAUTH': postiz's OPENAI_OAUTH_CLIENT_ID is the client id its own MCP OAuth SERVER
|
|
58
|
+
// accepts from the ChatGPT connector — inbound, no api.openai.com call — so it is ruled
|
|
59
|
+
// not-a-vendor in the packless registry instead.
|
|
60
|
+
envStems: ['OPENAI', 'APOPENAI', 'OPENAIMODERATION', 'REWORKDPLATFORMOPENAI'],
|
|
61
|
+
},
|
|
62
|
+
// The whole REST surface the `openai` SDK talks to uses one base, https://api.openai.com — the
|
|
63
|
+
// same default the pack's own connector pins (openai-connector.ts).
|
|
64
|
+
hosts: [{ host: 'api.openai.com' }, { host: 'platform.openai.com', pathPattern: '^/(settings/proj[-_][^/]+/api-keys|login$)' }],
|
|
65
|
+
// The SDK calls the same-origin '/v1/…' and loads from api.openai.com — the dev proxy
|
|
66
|
+
// forwards '/v1/' to the twin and strips the absolute host so calls come back same-origin.
|
|
67
|
+
browserRouting: { apiPathPrefix: '/v1/', loaderHost: 'https://api.openai.com' },
|
|
68
|
+
};
|
|
69
|
+
export { createOpenAIScenarioEngine, loadOpenAIScenarioDocument, openaiScenarioAdapter, realizeOpenAIRespond } from "./openai-scenario.js";
|
|
70
|
+
// PROTOCOL 2: the pack registers itself on load — its rate budget and its state system (the head
|
|
71
|
+
// resolves `stateSystemFor(vendor)` in the process that serves the twin).
|
|
72
|
+
registerPack(pack);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DerivedManifest } from '@volter/world-core';
|
|
2
|
+
/** A file attached to a store as OpenAI shows it: its id is the file's, not the twin's composite key. */
|
|
3
|
+
export declare function vectorStoreFileView(body: Record<string, unknown>, subject: {
|
|
4
|
+
id: string;
|
|
5
|
+
}): Record<string, unknown>;
|
|
6
|
+
export declare const manifest: DerivedManifest;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
const vendor = (from, to, source, effects) => ({ actor: 'vendor', from, to, source, ...(effects ? { effects } : {}) });
|
|
2
|
+
const time = (from, to, source, effects) => ({ actor: 'time', from, to, source, ...(effects ? { effects } : {}) });
|
|
3
|
+
/** A Batch's `status`. OpenAI validates, runs and finalizes a batch on its own; the twin does that work
|
|
4
|
+
* when a read first looks (semantics/progress.ts). Only a batch still in flight can be cancelled: it is
|
|
5
|
+
* `cancelling` until OpenAI stops it (https://platform.openai.com/docs/guides/batch#batch-object). */
|
|
6
|
+
const BATCH_DOCS = 'spec:/components/schemas/Batch/properties/status "The current status of the batch."';
|
|
7
|
+
const batchStatus = {
|
|
8
|
+
initial: 'validating',
|
|
9
|
+
transitions: [
|
|
10
|
+
{
|
|
11
|
+
operation: 'cancelBatch',
|
|
12
|
+
from: ['validating', 'in_progress', 'finalizing'],
|
|
13
|
+
to: 'cancelling',
|
|
14
|
+
effects: { cancelling_at: { now: true } },
|
|
15
|
+
refusal: { status: 409, message: "Cannot cancel a batch with status '{from}'." },
|
|
16
|
+
source: 'spec:cancelBatch "The batch will be in status `cancelling` for up to 10 minutes, before changing to `cancelled`"',
|
|
17
|
+
},
|
|
18
|
+
vendor(['validating'], 'in_progress', BATCH_DOCS, { in_progress_at: { now: true } }),
|
|
19
|
+
vendor(['in_progress'], 'finalizing', BATCH_DOCS, { finalizing_at: { now: true } }),
|
|
20
|
+
vendor(['finalizing'], 'completed', BATCH_DOCS, { completed_at: { now: true } }),
|
|
21
|
+
vendor(['cancelling'], 'cancelled', BATCH_DOCS, { cancelled_at: { now: true } }),
|
|
22
|
+
// OpenAI also fails a batch whose input does not validate and expires one not done in its completion
|
|
23
|
+
// window; the twin validates every input and works a batch the moment a read looks, so it does neither
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
/** A fine-tuning job's `status`. OpenAI validates the files, queues and trains on its own; the twin
|
|
27
|
+
* finishes that work when a read first looks, unless the job is paused. Cancel stops a job still in
|
|
28
|
+
* flight, pause one not yet finished, resume a paused one. `paused` is OpenAI's, though its spec's
|
|
29
|
+
* enum omits it. A resumed job is queued again. */
|
|
30
|
+
const invalidStatus = (verb) => ({ status: 400, code: 'invalid_status', message: `Cannot ${verb} a job with status '{from}'.` });
|
|
31
|
+
const FT_DOCS = 'spec:/components/schemas/FineTuningJob/properties/status "The current status of the fine-tuning job"';
|
|
32
|
+
const fineTuningJobStatus = {
|
|
33
|
+
initial: 'validating_files',
|
|
34
|
+
transitions: [
|
|
35
|
+
{ operation: 'cancelFineTuningJob', from: ['validating_files', 'queued', 'running', 'paused'], to: 'cancelled', refusal: invalidStatus('cancel'), source: 'spec:cancelFineTuningJob "Immediately cancel a fine-tune job."' },
|
|
36
|
+
{ operation: 'pauseFineTuningJob', from: ['validating_files', 'queued', 'running'], to: 'paused', refusal: invalidStatus('pause'), source: 'spec:pauseFineTuningJob "Pause a fine-tune job."' },
|
|
37
|
+
// a resumed job queues again (the reference's resume example answers `"status": "queued"`)
|
|
38
|
+
{ operation: 'resumeFineTuningJob', from: ['paused'], to: 'queued', refusal: invalidStatus('resume'), source: 'spec:resumeFineTuningJob "Resume a fine-tune job."' },
|
|
39
|
+
vendor(['validating_files'], 'queued', FT_DOCS),
|
|
40
|
+
vendor(['queued'], 'running', FT_DOCS),
|
|
41
|
+
vendor(['running'], 'succeeded', FT_DOCS, { finished_at: { now: true } }),
|
|
42
|
+
// OpenAI also fails a job whose files do not validate; the twin trains on any file, so none fails
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
/** A response's `status`, for the GA and the beta operations alike. A foreground response is answered
|
|
46
|
+
* `completed`; a background one is created `queued` and OpenAI works it on its own, over time on the
|
|
47
|
+
* World clock. Only a queued or in-progress one can be cancelled, and cancelling a
|
|
48
|
+
* cancelled one again answers it as it is (https://platform.openai.com/docs/guides/background). */
|
|
49
|
+
const BACKGROUND = 'https://platform.openai.com/docs/guides/background';
|
|
50
|
+
const responseStatus = (prefix) => ({
|
|
51
|
+
initial: 'completed',
|
|
52
|
+
transitions: [
|
|
53
|
+
{
|
|
54
|
+
operation: `${prefix}cancelResponse`,
|
|
55
|
+
from: ['queued', 'in_progress'],
|
|
56
|
+
to: 'cancelled',
|
|
57
|
+
refusal: { status: 400, code: 'invalid_status', message: "Cannot cancel a response with status '{from}'." },
|
|
58
|
+
source: 'spec:cancelResponse "Only responses created with the `background` parameter set to `true` can be cancelled."',
|
|
59
|
+
},
|
|
60
|
+
{ operation: `${prefix}cancelResponse`, from: ['cancelled'], source: BACKGROUND },
|
|
61
|
+
// OpenAI writes a background response over time: queued, in progress, completed (semantics/responses.ts
|
|
62
|
+
// times it); it may also fail it or leave it incomplete, which a stub that runs no model never does
|
|
63
|
+
vendor(['queued'], 'in_progress', BACKGROUND),
|
|
64
|
+
vendor(['queued', 'in_progress'], 'completed', BACKGROUND),
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
/** A run's `status`. OpenAI queues a run and works it on its own, stopping at `requires_action` for
|
|
68
|
+
* its function tools' outputs; the twin works it when a read first looks: a run with function tools
|
|
69
|
+
* stops for them (the placeholder model calls them), then, once they are submitted, ends with a labeled
|
|
70
|
+
* stub reply. Only a run in flight can be cancelled (`cancelling` until OpenAI stops it), and only one
|
|
71
|
+
* waiting on tools takes outputs. */
|
|
72
|
+
const RUN_DOCS = 'spec:/components/schemas/RunObject/properties/status "The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, `incomplete`"';
|
|
73
|
+
const runStatus = {
|
|
74
|
+
initial: 'queued',
|
|
75
|
+
transitions: [
|
|
76
|
+
{
|
|
77
|
+
operation: 'cancelRun',
|
|
78
|
+
from: ['queued', 'in_progress', 'requires_action'],
|
|
79
|
+
to: 'cancelling',
|
|
80
|
+
// a run being cancelled needs no action ("Will be `null` if no action is required", RunObject.required_action)
|
|
81
|
+
effects: { required_action: { value: null } },
|
|
82
|
+
refusal: { status: 400, message: "Cannot cancel run with status '{from}'." },
|
|
83
|
+
source: 'spec:cancelRun "Cancels a run that is `in_progress`."',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
operation: 'submitToolOuputsToRun',
|
|
87
|
+
from: ['requires_action'],
|
|
88
|
+
to: 'queued',
|
|
89
|
+
refusal: { status: 400, message: 'Runs in status "{from}" do not accept tool outputs.' },
|
|
90
|
+
source: 'spec:submitToolOuputsToRun "When a run has the `status: \"requires_action\"`"',
|
|
91
|
+
},
|
|
92
|
+
vendor(['queued'], 'in_progress', RUN_DOCS, { started_at: { now: true } }),
|
|
93
|
+
vendor(['in_progress'], 'completed', RUN_DOCS, { completed_at: { now: true } }),
|
|
94
|
+
// a run whose model calls its function tools stops for their outputs
|
|
95
|
+
// (https://platform.openai.com/docs/assistants/tools/function-calling)
|
|
96
|
+
vendor(['in_progress'], 'requires_action', RUN_DOCS),
|
|
97
|
+
vendor(['cancelling'], 'cancelled', RUN_DOCS, { cancelled_at: { now: true } }),
|
|
98
|
+
// OpenAI also fails a run, leaves it incomplete or expires it; the twin's stub model never does
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
/** A project's `status`: archive moves any project to `archived`, stamping when. */
|
|
102
|
+
const projectStatus = {
|
|
103
|
+
initial: 'active',
|
|
104
|
+
transitions: [
|
|
105
|
+
{ operation: 'archive-project', from: '*', to: 'archived', effects: { archived_at: { now: true } }, source: 'spec:archive-project "Archives a project in the organization. Archived projects cannot be used or updated."' },
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
/** A vector store's `status`: it is `in_progress` while OpenAI embeds files added to it, and expires
|
|
109
|
+
* when its `expires_after` policy runs out. The twin embeds nothing, so a store stays `completed`. */
|
|
110
|
+
// the twin neither embeds nor keeps an expiry policy, so no move of a store's status is the twin's
|
|
111
|
+
// (OpenAI's: "The status of the vector store, which can be either `expired`, `in_progress`, or `completed`.")
|
|
112
|
+
const vectorStoreStatus = { initial: 'completed', transitions: [] };
|
|
113
|
+
/** A file attached to a store, or a batch of them: OpenAI processes it on its own (the twin, when a read
|
|
114
|
+
* first looks); cancelling the batch cancels what is still in progress, and a finished batch is refused. */
|
|
115
|
+
// Extrapolation: the wording of OpenAI's refusal to cancel a finished batch or eval run is not sourced from
|
|
116
|
+
// a recording; that only one in progress can be cancelled is (the spec's operation descriptions).
|
|
117
|
+
const notInFlight = (what) => ({ status: 400, message: `Cannot cancel ${what} with status '{from}'.` });
|
|
118
|
+
const vectorStoreFileStatus = (source, refusal) => ({
|
|
119
|
+
initial: 'in_progress',
|
|
120
|
+
transitions: [
|
|
121
|
+
// the batch is the cancel's subject (its files stop when the cancel lands, the vendor's move below)
|
|
122
|
+
...(refusal ? [{ operation: 'cancelVectorStoreFileBatch', from: ['in_progress'], to: 'cancelled', refusal, source: 'spec:cancelVectorStoreFileBatch "Cancel a vector store file batch."' }] : []),
|
|
123
|
+
vendor(['in_progress'], 'completed', source),
|
|
124
|
+
// a cancel asked for lands once the work in flight is done (semantics/vector-stores.ts)
|
|
125
|
+
vendor(['in_progress'], 'cancelled', 'spec:cancelVectorStoreFileBatch "This attempts to cancel the processing of files in this batch as soon as possible."'),
|
|
126
|
+
// OpenAI fails a file it cannot parse; the twin embeds nothing, so none fails
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
/** An Upload's `status` (the spec declares no Upload resource: semantics/uploads.ts serves it). Parts
|
|
130
|
+
* are added and it is completed or cancelled only while `pending`; an hour after creation it expires.
|
|
131
|
+
* OpenAI answers a finished Upload as though it were not there. */
|
|
132
|
+
const UPLOAD_DOCS = 'spec:/components/schemas/Upload/properties/status "The status of the Upload."';
|
|
133
|
+
const noUpload = { status: 404, message: 'No such Upload object: {id}' };
|
|
134
|
+
const uploadStatus = {
|
|
135
|
+
initial: 'pending',
|
|
136
|
+
transitions: [
|
|
137
|
+
{ operation: 'addUploadPart', from: ['pending'], refusal: noUpload, source: 'spec:addUploadPart "Adds a [Part]"' },
|
|
138
|
+
{ operation: 'completeUpload', from: ['pending'], to: 'completed', refusal: noUpload, source: 'spec:completeUpload "Completes the [Upload]"' },
|
|
139
|
+
{ operation: 'cancelUpload', from: ['pending'], to: 'cancelled', refusal: noUpload, source: 'spec:cancelUpload "No Parts may be added after an Upload is cancelled."' },
|
|
140
|
+
time(['pending'], 'expired', UPLOAD_DOCS),
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
/** A container's `status`: running while in use, expired once idle past its `expires_after` (by default
|
|
144
|
+
* 20 minutes after `last_active_at`), observed when a read or a use looks (semantics/containers.ts).
|
|
145
|
+
* An expired container's files can no longer be listed, read, added or removed. */
|
|
146
|
+
const CONTAINERS = 'https://platform.openai.com/docs/guides/tools-code-interpreter#containers';
|
|
147
|
+
// Extrapolation: the status, code and wording of OpenAI's refusal to use an expired container are not
|
|
148
|
+
// sourced from a recording; the rule that an expired container cannot be used is (CONTAINERS).
|
|
149
|
+
const containerExpired = { status: 400, code: 'container_expired', message: 'Container {id} is expired.' };
|
|
150
|
+
const inRunning = (operation) => ({ operation, from: ['running'], refusal: containerExpired, source: CONTAINERS });
|
|
151
|
+
const containerStatus = {
|
|
152
|
+
initial: 'running',
|
|
153
|
+
transitions: [
|
|
154
|
+
time(['running'], 'expired', CONTAINERS),
|
|
155
|
+
...['CreateContainerFile', 'ListContainerFiles', 'RetrieveContainerFile', 'RetrieveContainerFileContent', 'DeleteContainerFile'].map(inRunning),
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
/** An eval run's `status`: OpenAI queues and grades it on its own; the twin grades it (a stub grader)
|
|
159
|
+
* when a read first looks. A run in flight can be cancelled. */
|
|
160
|
+
const EVAL_DOCS = 'spec:/components/schemas/EvalRun/properties/status "The status of the evaluation run."';
|
|
161
|
+
const evalRunStatus = {
|
|
162
|
+
initial: 'queued',
|
|
163
|
+
transitions: [
|
|
164
|
+
{ operation: 'cancelEvalRun', from: ['queued', 'in_progress'], to: 'canceled', refusal: notInFlight('an eval run'), source: 'spec:cancelEvalRun "Cancel an ongoing evaluation run."' },
|
|
165
|
+
vendor(['queued'], 'in_progress', EVAL_DOCS),
|
|
166
|
+
vendor(['in_progress'], 'completed', EVAL_DOCS),
|
|
167
|
+
// OpenAI fails a run whose data source or model errors; the twin grades with a stub, which never does
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
/** A file attached to a store as OpenAI shows it: its id is the file's, not the twin's composite key. */
|
|
171
|
+
export function vectorStoreFileView(body, subject) {
|
|
172
|
+
return { id: subject.id.split('::')[1] ?? subject.id, object: 'vector_store.file', vector_store_id: body.vector_store_id, created_at: body.created_at, status: body.status, usage_bytes: body.usage_bytes ?? 0, last_error: body.last_error ?? null, ...(body.attributes ? { attributes: body.attributes } : {}) };
|
|
173
|
+
}
|
|
174
|
+
/** OpenAI's list order: newest first by creation time. */
|
|
175
|
+
const NEWEST = { field: 'created_at', direction: 'desc' };
|
|
176
|
+
/** OpenAI's deletion answer names the deleted object's own type (`vector_store.deleted`). */
|
|
177
|
+
const deletedAs = (object) => ({ id: '{id}', object, deleted: true });
|
|
178
|
+
const underStore = { param: 'vector_store_id', field: 'vector_store_id', resource: 'VectorStoreObject' };
|
|
179
|
+
const underThread = { param: 'thread_id', field: 'thread_id', resource: 'ThreadObject' };
|
|
180
|
+
export const manifest = {
|
|
181
|
+
vendor: 'openai',
|
|
182
|
+
service: 'openai',
|
|
183
|
+
body: {},
|
|
184
|
+
ids: { template: '{prefix}-twin-{n}' },
|
|
185
|
+
// The screens people reach (docs/contributing/architecture.md, "Screens"): a project's secret keys are made on the
|
|
186
|
+
// dashboard's API keys page, never through the API, which only lists, reads and deletes them.
|
|
187
|
+
screens: [
|
|
188
|
+
{
|
|
189
|
+
id: 'api-keys', kind: 'workspace', host: 'platform.openai.com', path: '/settings/{project}/api-keys', status: 'done',
|
|
190
|
+
demand: 'every application that calls the API runs on a key a person made on this page',
|
|
191
|
+
controls: ['Name', 'Permissions', 'Create secret key', 'Revoke key'],
|
|
192
|
+
source: 'https://platform.openai.com/docs/api-reference/project-api-keys',
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
time: 'unix',
|
|
196
|
+
error: { error: { message: '{message}', type: '{kind}', param: '{param}', code: '{code}' } },
|
|
197
|
+
defaultKind: 'invalid_request_error',
|
|
198
|
+
readOnly: { status: 405, code: 'method_not_allowed', message: 'twin is read-only; omit readOnly to accept writes' },
|
|
199
|
+
notFound: { status: 404, message: "No such {object}: '{id}'" },
|
|
200
|
+
list: {
|
|
201
|
+
style: 'envelope',
|
|
202
|
+
envelope: { object: 'list', data: '{data}', has_more: '{has_more}', first_id: '{first_id}', last_id: '{last_id}' },
|
|
203
|
+
limit: { param: 'limit', default: 20, max: 100 },
|
|
204
|
+
after: 'after',
|
|
205
|
+
before: 'before',
|
|
206
|
+
unknownCursor: 'end',
|
|
207
|
+
},
|
|
208
|
+
deleted: { id: '{id}', object: '{object}', deleted: true },
|
|
209
|
+
// OpenAI requires a bearer key on every request. The twin cannot check real keys, so it answers
|
|
210
|
+
// the checkable failures: none, or a key it reserves as invalid.
|
|
211
|
+
auth: {
|
|
212
|
+
header: 'authorization',
|
|
213
|
+
scheme: 'Bearer',
|
|
214
|
+
gateWhenAbsent: true,
|
|
215
|
+
missing: { status: 401, code: 'invalid_api_key', message: "You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY)." },
|
|
216
|
+
invalidKeys: ['sk-invalid', 'invalid'],
|
|
217
|
+
invalid: { status: 401, code: 'invalid_api_key', message: 'Incorrect API key provided. You can find your API key at https://platform.openai.com/account/api-keys.' },
|
|
218
|
+
},
|
|
219
|
+
answerHeaders: { 'x-request-id': 'req_twin' },
|
|
220
|
+
sse: { named: false, done: '[DONE]' },
|
|
221
|
+
// a response streams named events (`event: response.created`) and ends with `response.completed`, no sentinel
|
|
222
|
+
// (https://platform.openai.com/docs/api-reference/responses-streaming; the reference's Streaming example)
|
|
223
|
+
// a streamed transcription is `data:` events ending with `transcript.text.done`, no sentinel (the reference's
|
|
224
|
+
// Streaming transcription example); a streamed image is named events ending with its `completed` event
|
|
225
|
+
// (https://platform.openai.com/docs/api-reference/images-streaming)
|
|
226
|
+
streamFor: { createResponse: { named: true }, beta_createResponse: { named: true }, createTranscription: { named: false }, createImage: { named: true }, createImageEdit: { named: true } },
|
|
227
|
+
// operations of declared resources the twin does not model: OpenAI's unknown-URL answer, not the core's
|
|
228
|
+
unmodeled: [
|
|
229
|
+
'updateChatCompletion',
|
|
230
|
+
'modifyVectorStore', 'updateVectorStoreFileAttributes',
|
|
231
|
+
'modifyMessage', 'deleteMessage', 'modifyRun',
|
|
232
|
+
'updateEval', 'deleteEval', 'deleteEvalRun', 'getEvalRunOutputItem',
|
|
233
|
+
],
|
|
234
|
+
// a search is a POST that only reads: a read-only twin answers it
|
|
235
|
+
reads: ['searchVectorStore'],
|
|
236
|
+
resources: {
|
|
237
|
+
// the catalog is static data and fine-tuned models are read off their jobs; a row here is only
|
|
238
|
+
// a deletion (semantics/models.ts serves every operation)
|
|
239
|
+
Model: { storedAs: 'model', idPrefix: 'model', notFound: { code: 'model_not_found', message: "The model '{id}' does not exist" } },
|
|
240
|
+
// `status` is deprecated on a File (always `processed` here) and `purpose` is what it is for:
|
|
241
|
+
// neither moves (https://platform.openai.com/docs/api-reference/files/object)
|
|
242
|
+
OpenAIFile: { storedAs: 'file', idPrefix: 'file', notFound: 'No such File object: {id}', notState: ['purpose', 'status'], order: NEWEST },
|
|
243
|
+
FineTuningJob: { storedAs: 'fine_tuning_job', idPrefix: 'ftjob', notFound: 'No such fine-tuning job: {id}', state: { status: fineTuningJobStatus }, order: NEWEST },
|
|
244
|
+
// read off their job, never stored (semantics/fine-tuning.ts)
|
|
245
|
+
FineTuningJobEvent: { idPrefix: 'ftevent', notState: ['level', 'type'] },
|
|
246
|
+
FineTuningJobCheckpoint: { idPrefix: 'ftckpt' },
|
|
247
|
+
// a store and what it holds are born completed (the twin embeds nothing)
|
|
248
|
+
VectorStoreObject: { storedAs: 'vector_store', idPrefix: 'vs', notFound: 'No such vector store: {id}', deleted: deletedAs('vector_store.deleted'), state: { status: vectorStoreStatus }, order: NEWEST },
|
|
249
|
+
// one file may sit in several stores, so an attached file is keyed by both and shown by the file's id
|
|
250
|
+
VectorStoreFileObject: {
|
|
251
|
+
storedAs: 'vector_store_file',
|
|
252
|
+
idPrefix: 'vsf',
|
|
253
|
+
parent: underStore,
|
|
254
|
+
key: '{vector_store_id}::{file_id}',
|
|
255
|
+
view: vectorStoreFileView,
|
|
256
|
+
state: { status: vectorStoreFileStatus('spec:/components/schemas/VectorStoreFileObject/properties/status "The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`."') },
|
|
257
|
+
},
|
|
258
|
+
VectorStoreFileBatchObject: {
|
|
259
|
+
storedAs: 'vector_store_file_batch',
|
|
260
|
+
idPrefix: 'vsfb',
|
|
261
|
+
notFound: 'No such file batch: {id}',
|
|
262
|
+
parent: underStore,
|
|
263
|
+
state: { status: vectorStoreFileStatus('spec:/components/schemas/VectorStoreFileBatchObject/properties/status "The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`."', notInFlight('a vector store file batch')) },
|
|
264
|
+
},
|
|
265
|
+
// every call is recorded; only a `store: true` one reads back (semantics/chat-completions.ts)
|
|
266
|
+
CreateChatCompletionResponse: {
|
|
267
|
+
storedAs: 'chat_completion',
|
|
268
|
+
idPrefix: 'chatcmpl',
|
|
269
|
+
readableWhen: { _stored: true },
|
|
270
|
+
notFound: "No chat completion found with id '{id}'.",
|
|
271
|
+
deleted: deletedAs('chat.completion.deleted'),
|
|
272
|
+
order: { field: 'created', direction: 'desc' },
|
|
273
|
+
},
|
|
274
|
+
// a stored response and its input items (semantics/responses.ts); one tree type for both surfaces. Its deletion
|
|
275
|
+
// answers `"object": "response"` (the reference's delete example: https://platform.openai.com/docs/api-reference/responses/delete)
|
|
276
|
+
Response: { storedAs: 'response', idPrefix: 'resp', notFound: "Response with id '{id}' not found.", deleted: deletedAs('response'), state: { status: responseStatus('') } },
|
|
277
|
+
BetaResponse: { storedAs: 'response', idPrefix: 'resp', notFound: "Response with id '{id}' not found.", deleted: deletedAs('response'), state: { status: responseStatus('beta_') } },
|
|
278
|
+
// the items live inside their response, never stored on their own: who wrote one, where a tool
|
|
279
|
+
// call ran, and how far generation got are what it is, and the twin's are whole
|
|
280
|
+
ItemResource: { idPrefix: 'item', notState: ['execution', 'role', 'status'] },
|
|
281
|
+
BetaItemResource: { idPrefix: 'item', notState: ['execution', 'role', 'status'] },
|
|
282
|
+
// the Assistants API (semantics/assistants.ts); messages and runs live under their thread
|
|
283
|
+
AssistantObject: { storedAs: 'assistant', idPrefix: 'asst', notFound: "No assistant found with id '{id}'.", deleted: deletedAs('assistant.deleted'), order: NEWEST },
|
|
284
|
+
ThreadObject: { storedAs: 'thread', idPrefix: 'thread', notFound: "No thread found with id '{id}'.", deleted: deletedAs('thread.deleted') },
|
|
285
|
+
// a message's role is who wrote it and its status is not modeled (every message is whole)
|
|
286
|
+
MessageObject: { storedAs: 'message', idPrefix: 'msg', notFound: "No message found with id '{id}'.", parent: underThread, notState: ['role', 'status'], order: NEWEST },
|
|
287
|
+
RunObject: { storedAs: 'run', idPrefix: 'run', notFound: "No run found with id '{id}'.", parent: underThread, state: { status: runStatus }, order: NEWEST },
|
|
288
|
+
// a run's steps are read off the run, never stored, so neither field moves on its own
|
|
289
|
+
RunStepObject: { idPrefix: 'step', notState: ['status', 'type'] },
|
|
290
|
+
// the Admin API (semantics/organization.ts); residency and a key's project access are settings
|
|
291
|
+
Project: { storedAs: 'project', idPrefix: 'proj', notFound: 'Project {id} not found', state: { status: projectStatus }, notState: ['residency'], order: NEWEST },
|
|
292
|
+
ProjectApiKey: {
|
|
293
|
+
storedAs: 'api_key',
|
|
294
|
+
idPrefix: 'key',
|
|
295
|
+
notFound: 'API key {id} not found',
|
|
296
|
+
parent: { param: 'project_id', field: '_project_id', resource: 'Project' },
|
|
297
|
+
deleted: deletedAs('organization.project.api_key.deleted'),
|
|
298
|
+
notState: ['owner_project_access'],
|
|
299
|
+
order: NEWEST,
|
|
300
|
+
},
|
|
301
|
+
// computed from the recorded usage, never stored
|
|
302
|
+
UsageTimeBucket: { idPrefix: 'bucket' },
|
|
303
|
+
// the Evals API (semantics/evals.ts); a run lives under its eval, its output items are read off it
|
|
304
|
+
Eval: { storedAs: 'eval', idPrefix: 'eval', notFound: 'Eval {id} not found', order: NEWEST },
|
|
305
|
+
EvalRun: { storedAs: 'eval_run', idPrefix: 'evalrun', notFound: 'Eval run {id} not found', parent: { param: 'eval_id', field: 'eval_id', resource: 'Eval' }, state: { status: evalRunStatus }, order: NEWEST },
|
|
306
|
+
// an output item's status is its grade (pass or fail), not a lifecycle
|
|
307
|
+
EvalRunOutputItem: { idPrefix: 'evalitem', notState: ['status'] },
|
|
308
|
+
// code-interpreter sandboxes (semantics/containers.ts); a file lives under its container, and a
|
|
309
|
+
// container's memory_limit is a setting
|
|
310
|
+
ContainerResource: { storedAs: 'container', idPrefix: 'cntr', notFound: 'Container {id} not found', deleted: deletedAs('container.deleted'), state: { status: containerStatus }, notState: ['memory_limit'], order: NEWEST },
|
|
311
|
+
ContainerFileResource: {
|
|
312
|
+
storedAs: 'container_file',
|
|
313
|
+
idPrefix: 'cfile',
|
|
314
|
+
notFound: 'Container file {id} not found',
|
|
315
|
+
parent: { param: 'container_id', field: 'container_id', resource: 'ContainerResource' },
|
|
316
|
+
deleted: deletedAs('container.file.deleted'),
|
|
317
|
+
order: NEWEST,
|
|
318
|
+
},
|
|
319
|
+
Batch: { storedAs: 'batch', idPrefix: 'batch', notFound: 'No such Batch object: {id}', state: { status: batchStatus }, order: NEWEST },
|
|
320
|
+
// a large file sent in parts (semantics/uploads.ts); the spec names no resource for it
|
|
321
|
+
Upload: { storedAs: 'upload', idPrefix: 'upload', notFound: 'No such Upload object: {id}', state: { status: uploadStatus } },
|
|
322
|
+
},
|
|
323
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { RateBudget, type RateBudgetDeclaration, type RateBudgetOptions, type RateBudgetReservation, type RateBudgetSnapshot } from '@volter/world-core';
|
|
2
|
+
/** Rolling window, in ms. Spend older than this is pruned. */
|
|
3
|
+
export declare const OPENAI_BUDGET_WINDOW_MS = 60000;
|
|
4
|
+
/**
|
|
5
|
+
* Weighted units allowed inside one window. 60/60s at `defaultWeight` 2 = 30 calls a minute —
|
|
6
|
+
* EXACTLY the kernel's undeclared fallback, because OpenAI publishes no scalar that would justify
|
|
7
|
+
* more. See the header.
|
|
8
|
+
*/
|
|
9
|
+
export declare const OPENAI_BUDGET_CEILING = 60;
|
|
10
|
+
/** Seconds. A `retry-after` above this means the key is throttled hard — fail loudly, don't sleep. */
|
|
11
|
+
export declare const OPENAI_BUDGET_MAX_RETRY_AFTER_S = 300;
|
|
12
|
+
/** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for what is documented vs. judged. */
|
|
13
|
+
export declare const OPENAI_CALL_WEIGHTS: {
|
|
14
|
+
/** `/v1/chat/completions`, `/v1/responses`, `/v1/embeddings`, `/v1/images/*`, `/v1/audio/*` —
|
|
15
|
+
* token/compute-metered, where TPM rather than RPM is the binding limit. */
|
|
16
|
+
readonly inference: 5;
|
|
17
|
+
/** `POST /v1/vector_stores/:id/files` — documented 300 RPM per store, and a per-file fan-out. */
|
|
18
|
+
readonly ingest: 4;
|
|
19
|
+
/** Everything else: file/batch/fine-tune/vector-store list, retrieve, create, cancel, delete. */
|
|
20
|
+
readonly other: 2;
|
|
21
|
+
};
|
|
22
|
+
/** THE PACK'S DECLARATION — pure data, the only OpenAI-specific thing in the whole budget. */
|
|
23
|
+
export declare const OPENAI_RATE_BUDGET: RateBudgetDeclaration;
|
|
24
|
+
/**
|
|
25
|
+
* Price one call. The key is `"<METHOD> <path>"` with the query string split off, so a rule can
|
|
26
|
+
* price by method (a write is not a read) without the kernel knowing anything about OpenAI. An
|
|
27
|
+
* unclassified endpoint still costs `defaultWeight` — nothing is ever free.
|
|
28
|
+
*/
|
|
29
|
+
export declare function openaiCallWeight(method: string, path: string): number;
|
|
30
|
+
/** Where OpenAI's ledger lives. Token-keyed and cwd-independent by default (the limit is per
|
|
31
|
+
* organization/project, i.e. per key, so a cwd-scoped ledger would hand the same key a fresh
|
|
32
|
+
* allowance in every checkout, worktree and CI matrix leg); pass `root` for world-scoped accounting. */
|
|
33
|
+
export declare function openaiBudgetPath(opts?: {
|
|
34
|
+
root?: string;
|
|
35
|
+
token?: string;
|
|
36
|
+
} | string): string;
|
|
37
|
+
/** Construction options for OpenAI's budget. The vendor is fixed; everything else may only TIGHTEN. */
|
|
38
|
+
export type OpenAIBudgetOptions = Omit<RateBudgetOptions, 'vendor'>;
|
|
39
|
+
/**
|
|
40
|
+
* OpenAI's budget — the shared kernel guard bound to this vendor's declaration. A real subclass,
|
|
41
|
+
* not an alias, so `budget instanceof OpenAIBudget` in `liveOpenAIExecute` means "a budget that
|
|
42
|
+
* accounts against OPENAI's ledger under OPENAI's ceiling": another vendor's `RateBudget` (with its
|
|
43
|
+
* own, possibly larger, ceiling) is NOT assignable there.
|
|
44
|
+
*/
|
|
45
|
+
export declare class OpenAIBudget extends RateBudget {
|
|
46
|
+
constructor(opts?: OpenAIBudgetOptions);
|
|
47
|
+
}
|
|
48
|
+
/** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
|
|
49
|
+
* which one refused, and `err.kind` says why. */
|
|
50
|
+
export { RateBudgetError as OpenAIBudgetError } from '@volter/world-core';
|
|
51
|
+
export type { RateBudgetErrorKind as OpenAIBudgetErrorKind } from '@volter/world-core';
|
|
52
|
+
export type OpenAIBudgetReservation = RateBudgetReservation;
|
|
53
|
+
export type OpenAIBudgetSnapshot = RateBudgetSnapshot;
|