Delete unread model adapter status families
This commit is contained in:
parent
b987976b74
commit
0091e0e122
8 changed files with 6 additions and 267 deletions
|
|
@ -47,12 +47,10 @@ use cultcache_rs::CacheBackingStore;
|
|||
use cultcache_rs::CultCache;
|
||||
use cultcache_rs::CultCacheEnvelope;
|
||||
use cultcache_rs::DatabaseEntry;
|
||||
use epiphany_model_adapter::EpiphanyModelAdapterStatus;
|
||||
use epiphany_model_adapter::EpiphanyModelReceipt;
|
||||
use epiphany_model_adapter::EpiphanyModelRequest;
|
||||
use epiphany_model_adapter::EpiphanyModelStreamEvent;
|
||||
use epiphany_model_adapter::EpiphanyModelStreamPayload;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAdapterStatus;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelReceipt;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelRequest;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent;
|
||||
|
|
@ -88,7 +86,7 @@ pub const COORDINATOR_RUN_RECEIPT_TYPE: &str = "epiphany.coordinator_run_receipt
|
|||
pub const RUNTIME_IDENTITY_KEY: &str = "self";
|
||||
pub const RUNTIME_SWARM_BINDING_KEY: &str = "runtime-swarm-binding";
|
||||
pub const RUNTIME_SWARM_BINDING_SCHEMA_VERSION: &str = "epiphany.runtime.swarm_binding.v1";
|
||||
pub const RUNTIME_SPINE_SCHEMA_VERSION: &str = "epiphany.runtime_spine.v22";
|
||||
pub const RUNTIME_SPINE_SCHEMA_VERSION: &str = "epiphany.runtime_spine.v23";
|
||||
pub const EPIPHANY_RUNTIME_ROOT_SESSION_ID: &str = "epiphany-main";
|
||||
pub const RUNTIME_MODEL_EXECUTION_BINDING_SCHEMA_VERSION: &str =
|
||||
"epiphany.runtime.model_execution_binding.v0";
|
||||
|
|
@ -863,11 +861,9 @@ fn runtime_spine_schema_cache() -> Result<CultCache> {
|
|||
cache.register_entry_type::<HandsCommitReceipt>()?;
|
||||
cache.register_entry_type::<SoulVerdictReceipt>()?;
|
||||
cache.register_entry_type::<ContinuityRecoveryReceipt>()?;
|
||||
cache.register_entry_type::<EpiphanyOpenAiAdapterStatus>()?;
|
||||
cache.register_entry_type::<EpiphanyOpenAiModelRequest>()?;
|
||||
cache.register_entry_type::<EpiphanyOpenAiStreamEvent>()?;
|
||||
cache.register_entry_type::<EpiphanyOpenAiModelReceipt>()?;
|
||||
cache.register_entry_type::<EpiphanyModelAdapterStatus>()?;
|
||||
cache.register_entry_type::<EpiphanyModelRequest>()?;
|
||||
cache.register_entry_type::<EpiphanyModelStreamEvent>()?;
|
||||
cache.register_entry_type::<EpiphanyModelReceipt>()?;
|
||||
|
|
|
|||
|
|
@ -5,28 +5,6 @@ use serde::Serialize;
|
|||
pub const MODEL_ADAPTER_REQUEST_SCHEMA_ID: &str = "epiphany.model_request.v0";
|
||||
pub const MODEL_ADAPTER_EVENT_SCHEMA_ID: &str = "epiphany.model_stream_event.v0";
|
||||
pub const MODEL_ADAPTER_RECEIPT_SCHEMA_ID: &str = "epiphany.model_receipt.v0";
|
||||
pub const MODEL_ADAPTER_STATUS_SCHEMA_ID: &str = "epiphany.model_adapter_status.v0";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.model_adapter_status.v0",
|
||||
schema = "EpiphanyModelAdapterStatus"
|
||||
)]
|
||||
pub struct EpiphanyModelAdapterStatus {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_id: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub adapter_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub provider: String,
|
||||
#[cultcache(key = 3, default)]
|
||||
pub default_model: Option<String>,
|
||||
#[cultcache(key = 4)]
|
||||
pub streaming_supported: bool,
|
||||
#[cultcache(key = 5)]
|
||||
pub provider_transport_attached: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, DatabaseEntry)]
|
||||
#[cultcache(type = "epiphany.model_request.v0", schema = "EpiphanyModelRequest")]
|
||||
pub struct EpiphanyModelRequest {
|
||||
|
|
|
|||
|
|
@ -6,46 +6,12 @@ use serde::Serialize;
|
|||
pub const OPENAI_ADAPTER_REQUEST_SCHEMA_ID: &str = "epiphany.openai_model_request.v1";
|
||||
pub const OPENAI_ADAPTER_EVENT_SCHEMA_ID: &str = "epiphany.openai_model_stream_event.v0";
|
||||
pub const OPENAI_ADAPTER_RECEIPT_SCHEMA_ID: &str = "epiphany.openai_model_receipt.v0";
|
||||
pub const OPENAI_ADAPTER_STATUS_SCHEMA_ID: &str = "epiphany.openai_adapter_status.v0";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum EpiphanyOpenAiAuthMode {
|
||||
ChatGptSubscription,
|
||||
ApiKey,
|
||||
ExternalBearer,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum EpiphanyOpenAiWireDialect {
|
||||
Responses,
|
||||
ChatCompletionsTerminalTool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.openai_adapter_status.v0",
|
||||
schema = "EpiphanyOpenAiAdapterStatus"
|
||||
)]
|
||||
pub struct EpiphanyOpenAiAdapterStatus {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_id: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub adapter_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub auth_mode: EpiphanyOpenAiAuthMode,
|
||||
#[cultcache(key = 3, default)]
|
||||
pub account_id: Option<String>,
|
||||
#[cultcache(key = 4, default)]
|
||||
pub plan_type: Option<String>,
|
||||
#[cultcache(key = 5, default)]
|
||||
pub default_model: Option<String>,
|
||||
#[cultcache(key = 6)]
|
||||
pub supports_websockets: bool,
|
||||
#[cultcache(key = 7)]
|
||||
pub codex_transport_attached: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.openai_model_request.v1",
|
||||
|
|
|
|||
|
|
@ -9,18 +9,14 @@ use codex_client::Request;
|
|||
use codex_client::ReqwestTransport;
|
||||
use codex_client::TransportError;
|
||||
use codex_client::sse_stream;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAdapterStatus;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAuthMode;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiInputItem;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelReceipt;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelRequest;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamPayload;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiWireDialect;
|
||||
use epiphany_openai_adapter::OPENAI_ADAPTER_STATUS_SCHEMA_ID;
|
||||
use codex_login::AuthCredentialsStoreMode;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::AuthMode;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use serde::Deserialize;
|
||||
|
|
@ -37,8 +33,6 @@ const OPENROUTER_API_BASE_URL: &str = "https://openrouter.ai/api/v1";
|
|||
const RESPONSES_STREAM_IDLE_TIMEOUT: Duration = Duration::from_secs(45);
|
||||
const RESPONSES_CALL_ID_MAX_BYTES: usize = 64;
|
||||
|
||||
pub const CODEX_SPINE_ADAPTER_ID: &str = "codex-openai-subscription-spine";
|
||||
pub const OPENROUTER_SPINE_ADAPTER_ID: &str = "openrouter-chat-completions-spine";
|
||||
pub const OPENROUTER_TERMINAL_TOOL_NAME: &str = "epiphany_submit_typed_result";
|
||||
|
||||
pub fn default_codex_home() -> Result<std::path::PathBuf> {
|
||||
|
|
@ -60,47 +54,6 @@ pub fn auth_manager(codex_home: std::path::PathBuf) -> Arc<AuthManager> {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn status_from_auth_manager(
|
||||
auth_manager: &AuthManager,
|
||||
default_model: Option<String>,
|
||||
supports_websockets: bool,
|
||||
) -> EpiphanyOpenAiAdapterStatus {
|
||||
let auth = auth_manager.auth().await;
|
||||
let auth_mode = auth_mode_from_manager(auth_manager, auth.as_ref());
|
||||
let account_id = auth.as_ref().and_then(CodexAuth::get_account_id);
|
||||
let plan_type = auth
|
||||
.as_ref()
|
||||
.and_then(CodexAuth::account_plan_type)
|
||||
.map(|plan| format!("{plan:?}"));
|
||||
|
||||
EpiphanyOpenAiAdapterStatus {
|
||||
schema_id: OPENAI_ADAPTER_STATUS_SCHEMA_ID.to_string(),
|
||||
adapter_id: CODEX_SPINE_ADAPTER_ID.to_string(),
|
||||
auth_mode,
|
||||
account_id,
|
||||
plan_type,
|
||||
default_model,
|
||||
supports_websockets,
|
||||
codex_transport_attached: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_from_static_api_key(
|
||||
adapter_id: &str,
|
||||
default_model: Option<String>,
|
||||
) -> EpiphanyOpenAiAdapterStatus {
|
||||
EpiphanyOpenAiAdapterStatus {
|
||||
schema_id: OPENAI_ADAPTER_STATUS_SCHEMA_ID.to_string(),
|
||||
adapter_id: adapter_id.to_string(),
|
||||
auth_mode: EpiphanyOpenAiAuthMode::ApiKey,
|
||||
account_id: None,
|
||||
plan_type: None,
|
||||
default_model,
|
||||
supports_websockets: false,
|
||||
codex_transport_attached: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EpiphanyCodexOpenAiTransport {
|
||||
auth_manager: Arc<AuthManager>,
|
||||
base_url: Option<String>,
|
||||
|
|
@ -1457,33 +1410,6 @@ fn transport_error_to_anyhow(err: TransportError) -> anyhow::Error {
|
|||
anyhow::anyhow!("failed to open provider transport: {err}")
|
||||
}
|
||||
|
||||
fn auth_mode_from_manager(
|
||||
auth_manager: &AuthManager,
|
||||
auth: Option<&CodexAuth>,
|
||||
) -> EpiphanyOpenAiAuthMode {
|
||||
match auth_manager.auth_mode() {
|
||||
Some(AuthMode::ApiKey) => EpiphanyOpenAiAuthMode::ApiKey,
|
||||
Some(AuthMode::Chatgpt) | Some(AuthMode::ChatgptAuthTokens) => {
|
||||
EpiphanyOpenAiAuthMode::ChatGptSubscription
|
||||
}
|
||||
Some(AuthMode::AgentIdentity) => EpiphanyOpenAiAuthMode::ExternalBearer,
|
||||
None => auth_mode_from_codex_auth(auth),
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_mode_from_codex_auth(auth: Option<&CodexAuth>) -> EpiphanyOpenAiAuthMode {
|
||||
let Some(auth) = auth else {
|
||||
return EpiphanyOpenAiAuthMode::Unknown;
|
||||
};
|
||||
if auth.is_api_key_auth() {
|
||||
EpiphanyOpenAiAuthMode::ApiKey
|
||||
} else if auth.is_chatgpt_auth() {
|
||||
EpiphanyOpenAiAuthMode::ChatGptSubscription
|
||||
} else {
|
||||
EpiphanyOpenAiAuthMode::ExternalBearer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -1504,20 +1430,6 @@ mod tests {
|
|||
request
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_openrouter_status_does_not_claim_codex_transport() {
|
||||
let status = status_from_static_api_key(
|
||||
OPENROUTER_SPINE_ADAPTER_ID,
|
||||
Some("stealth/ox-alpha".to_string()),
|
||||
);
|
||||
|
||||
assert_eq!(status.adapter_id, OPENROUTER_SPINE_ADAPTER_ID);
|
||||
assert_eq!(status.auth_mode, EpiphanyOpenAiAuthMode::ApiKey);
|
||||
assert_eq!(status.default_model.as_deref(), Some("stealth/ox-alpha"));
|
||||
assert!(!status.supports_websockets);
|
||||
assert!(!status.codex_transport_attached);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openrouter_whole_request_timeout_is_explicit() -> Result<()> {
|
||||
let bounded = EpiphanyOpenRouterTransport::new("test-key", Some(Duration::from_secs(90)))?;
|
||||
|
|
|
|||
|
|
@ -31,17 +31,13 @@ use epiphany_model_adapter::EpiphanyModelRequest;
|
|||
use epiphany_model_adapter::EpiphanyModelStreamEvent;
|
||||
use epiphany_model_adapter::EpiphanyModelStreamPayload;
|
||||
use epiphany_model_adapter::EpiphanyModelToolDefinition;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAdapterStatus;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelRequest;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamPayload;
|
||||
use epiphany_openai_codex_spine::EpiphanyCodexOpenAiTransport;
|
||||
use epiphany_openai_codex_spine::EpiphanyOpenRouterTransport;
|
||||
use epiphany_openai_codex_spine::OPENROUTER_SPINE_ADAPTER_ID;
|
||||
use epiphany_openai_codex_spine::auth_manager;
|
||||
pub use epiphany_openai_codex_spine::default_codex_home;
|
||||
use epiphany_openai_codex_spine::status_from_auth_manager;
|
||||
use epiphany_openai_codex_spine::status_from_static_api_key;
|
||||
use epiphany_tool_adapter::EPIPHANY_TOOL_RUNTIME_ADAPTER_ID;
|
||||
use epiphany_tool_adapter::EpiphanyToolInvocationIntent;
|
||||
use epiphany_tool_adapter::EpiphanyToolInvocationReceipt;
|
||||
|
|
@ -78,10 +74,6 @@ impl EpiphanyProviderProfile {
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn streaming_supported(self) -> bool {
|
||||
matches!(self, Self::OpenAiCodex)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn worker_model_session_id(worker_job_id: &str) -> String {
|
||||
|
|
@ -171,41 +163,23 @@ async fn run_openai_model_turn_bound(
|
|||
&model_request,
|
||||
&now(),
|
||||
)?;
|
||||
let (status, events) = match profile {
|
||||
let events = match profile {
|
||||
EpiphanyProviderProfile::OpenAiCodex => {
|
||||
let auth_manager = auth_manager(options.codex_home.clone());
|
||||
let status =
|
||||
status_from_auth_manager(&auth_manager, options.default_model.clone(), true).await;
|
||||
let transport = EpiphanyCodexOpenAiTransport::openai(auth_manager);
|
||||
let events =
|
||||
collect_transport_events(transport.collect_model_events(request.clone()), &request)
|
||||
.await;
|
||||
(status, events)
|
||||
collect_transport_events(transport.collect_model_events(request.clone()), &request)
|
||||
.await
|
||||
}
|
||||
EpiphanyProviderProfile::OpenRouter => {
|
||||
let credential = read_static_provider_credential(
|
||||
options.provider_credential_path.as_deref(),
|
||||
OPENROUTER_MODEL_PROVIDER,
|
||||
)?;
|
||||
let status = status_from_static_api_key(
|
||||
OPENROUTER_SPINE_ADAPTER_ID,
|
||||
options.default_model.clone(),
|
||||
);
|
||||
let transport = EpiphanyOpenRouterTransport::new(credential, options.request_timeout)?;
|
||||
let events =
|
||||
collect_transport_events(transport.collect_model_events(request.clone()), &request)
|
||||
.await;
|
||||
(status, events)
|
||||
collect_transport_events(transport.collect_model_events(request.clone()), &request)
|
||||
.await
|
||||
}
|
||||
};
|
||||
store_openai_status(&options.store_path, &status)?;
|
||||
store_model_status(
|
||||
&options.store_path,
|
||||
&status,
|
||||
&provider,
|
||||
profile.streaming_supported(),
|
||||
true,
|
||||
)?;
|
||||
record_openai_events(&options.store_path, &options, &request, &provider, &events)
|
||||
}
|
||||
|
||||
|
|
@ -884,37 +858,6 @@ pub fn ensure_openai_runtime_ready(options: &EpiphanyOpenAiRuntimeOptions) -> Re
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_openai_status(
|
||||
store_path: impl AsRef<Path>,
|
||||
status: &EpiphanyOpenAiAdapterStatus,
|
||||
) -> Result<()> {
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
cache.put(status.adapter_id.clone(), status)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_model_status(
|
||||
store_path: impl AsRef<Path>,
|
||||
status: &EpiphanyOpenAiAdapterStatus,
|
||||
provider: &str,
|
||||
streaming_supported: bool,
|
||||
provider_transport_attached: bool,
|
||||
) -> Result<()> {
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let status = epiphany_model_adapter::EpiphanyModelAdapterStatus {
|
||||
schema_id: epiphany_model_adapter::MODEL_ADAPTER_STATUS_SCHEMA_ID.to_string(),
|
||||
adapter_id: status.adapter_id.clone(),
|
||||
provider: provider.to_string(),
|
||||
default_model: status.default_model.clone(),
|
||||
streaming_supported,
|
||||
provider_transport_attached,
|
||||
};
|
||||
cache.put(status.adapter_id.clone(), &status)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_model_request(
|
||||
store_path: impl AsRef<Path>,
|
||||
request: &EpiphanyModelRequest,
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.model-adapter-status.schema.json",
|
||||
"title": "Epiphany Model Adapter Status",
|
||||
"type": "object",
|
||||
"required": ["schema_id", "adapter_id", "provider", "streaming_supported", "provider_transport_attached"],
|
||||
"properties": {
|
||||
"schema_id": { "const": "epiphany.model_adapter_status.v0" },
|
||||
"adapter_id": { "type": "string" },
|
||||
"provider": { "type": "string" },
|
||||
"default_model": { "type": ["string", "null"] },
|
||||
"streaming_supported": { "type": "boolean" },
|
||||
"provider_transport_attached": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.openai-adapter-status.schema.json",
|
||||
"title": "Epiphany OpenAI Adapter Status",
|
||||
"type": "object",
|
||||
"required": ["schema_id", "adapter_id", "auth_mode", "supports_websockets", "codex_transport_attached"],
|
||||
"properties": {
|
||||
"schema_id": { "const": "epiphany.openai_adapter_status.v0" },
|
||||
"adapter_id": { "type": "string" },
|
||||
"auth_mode": { "enum": ["ChatGptSubscription", "ApiKey", "ExternalBearer", "Unknown"] },
|
||||
"account_id": { "type": ["string", "null"] },
|
||||
"plan_type": { "type": ["string", "null"] },
|
||||
"default_model": { "type": ["string", "null"] },
|
||||
"supports_websockets": { "type": "boolean" },
|
||||
"codex_transport_attached": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
@ -100,17 +100,6 @@
|
|||
"title": "Epiphany Coordinator Run Receipt",
|
||||
"path": "epiphany.coordinator-run-receipt.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.model-adapter-status.schema.json",
|
||||
"kind": "document_payload",
|
||||
"wireContracts": [
|
||||
"cultnet.schema.v0"
|
||||
],
|
||||
"schemaVersion": "epiphany.model_adapter_status.v0",
|
||||
"documentType": "epiphany.model_adapter_status.v0",
|
||||
"title": "Epiphany Model Adapter Status",
|
||||
"path": "epiphany.model-adapter-status.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.model-request.schema.json",
|
||||
"kind": "document_payload",
|
||||
|
|
@ -166,17 +155,6 @@
|
|||
"title": "Epiphany Tool Invocation Receipt",
|
||||
"path": "epiphany.tool-invocation-receipt.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.openai-adapter-status.schema.json",
|
||||
"kind": "document_payload",
|
||||
"wireContracts": [
|
||||
"cultnet.schema.v0"
|
||||
],
|
||||
"schemaVersion": "epiphany.openai_adapter_status.v0",
|
||||
"documentType": "epiphany.openai_adapter_status.v0",
|
||||
"title": "Epiphany OpenAI Adapter Status",
|
||||
"path": "epiphany.openai-adapter-status.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.openai-model-request.schema.json",
|
||||
"kind": "document_payload",
|
||||
|
|
|
|||
Reference in a new issue