Route strategic smoke through runtime provider
This commit is contained in:
parent
a266933ed9
commit
41f921cc47
4 changed files with 148 additions and 87 deletions
|
|
@ -1,25 +1,20 @@
|
|||
#[cfg(not(windows))]
|
||||
fn main() -> anyhow::Result<()> {
|
||||
anyhow::bail!("the live strategic smoke uses Starfire's DPAPI credential")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
use chrono::Utc;
|
||||
use ghostlight_dungeon::{
|
||||
domain::{TickSource, WorldCommand},
|
||||
kernel::{CommandResult, WorldKernel},
|
||||
model::{DeepSeekPort, ModelPort},
|
||||
model_runtime::ModelRuntimeSelection,
|
||||
persistence::CampaignStore,
|
||||
scheduler::propose_resolution_wave,
|
||||
turn::SnapshotPermit,
|
||||
};
|
||||
use std::{path::PathBuf, sync::Arc, time::Instant};
|
||||
|
||||
let secret = std::env::var_os("GHOSTLIGHT_DEEPSEEK_BLOB")
|
||||
let runtime_root = std::env::var_os("GHOSTLIGHT_DUNGEON_ROOT")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from(r"F:\GameCult\GhostlightDungeon\secrets\deepseek.dpapi"));
|
||||
.unwrap_or_else(default_runtime_root);
|
||||
let model_selection = ModelRuntimeSelection::from_environment(&runtime_root)?;
|
||||
let scenario_id = std::env::var("GHOSTLIGHT_LIVE_FIRE_SCENARIO")
|
||||
.unwrap_or_else(|_| "strategic-default".into());
|
||||
let pressure = std::env::var("GHOSTLIGHT_STRATEGIC_PRESSURE").unwrap_or_else(|_| {
|
||||
|
|
@ -53,7 +48,13 @@ async fn main() -> anyhow::Result<()> {
|
|||
.clone();
|
||||
let store = CampaignStore::open(root.join("campaign.cc"))?;
|
||||
store.create_unadmitted_fixture_campaign(&campaign, &[], &[])?;
|
||||
let model: Arc<dyn ModelPort> = Arc::new(DeepSeekPort::from_runtime_secret(secret)?);
|
||||
let model = model_selection.open()?.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} credential is unavailable at {}",
|
||||
model_selection.provider,
|
||||
model_selection.credential_path.display()
|
||||
)
|
||||
})?;
|
||||
let started = Instant::now();
|
||||
let output = propose_resolution_wave(
|
||||
model,
|
||||
|
|
@ -135,6 +136,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
"pressure":pressure,
|
||||
"campaign_id":campaign.id,
|
||||
"elapsed_seconds":started.elapsed().as_secs_f64(),
|
||||
"model_runtime":model_selection.status("configured"),
|
||||
"model_receipt_hash":output.aggregate_receipt_hash,
|
||||
"model_stage_receipts":output.stages.iter().map(|stage|&stage.receipt).collect::<Vec<_>>(),
|
||||
"plan":plan,
|
||||
|
|
@ -154,7 +156,17 @@ async fn main() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn default_runtime_root() -> std::path::PathBuf {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
std::path::PathBuf::from(r"F:\GameCult\GhostlightDungeon")
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
std::path::PathBuf::from("/var/lib/gamecult/ghostlight-dungeon")
|
||||
}
|
||||
}
|
||||
|
||||
fn strategic_campaign() -> ghostlight_dungeon::domain::Campaign {
|
||||
use chrono::{Duration, Utc};
|
||||
use ghostlight_dungeon::domain::*;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub mod legacy_transition;
|
|||
pub mod mesh;
|
||||
pub mod model;
|
||||
pub mod model_connector;
|
||||
pub mod model_runtime;
|
||||
pub mod newspaper;
|
||||
pub mod outcome;
|
||||
pub mod persistence;
|
||||
|
|
|
|||
|
|
@ -28,10 +28,8 @@ use ghostlight_dungeon::{
|
|||
CampaignMeshSnapshot, MeshPublisher, MeshRuntimeIdentity, PROVIDER_ID as EVE_PROVIDER_ID,
|
||||
SURFACE_ID as EVE_SURFACE_ID, SessionZeroMeshSnapshot,
|
||||
},
|
||||
model::{
|
||||
DeepSeekPort, MODEL_CAPABLE, MODEL_FAST, ModelPort, ModelRuntimeStatus, OpenRouterPort,
|
||||
},
|
||||
model_connector::CodexConnectorModelPort,
|
||||
model::{MODEL_CAPABLE, MODEL_FAST, ModelPort, ModelRuntimeStatus},
|
||||
model_runtime::ModelRuntimeSelection,
|
||||
persistence::CampaignStore,
|
||||
persona::PersonaProjectionEngine,
|
||||
registry::{CampaignRegistry, CampaignRuntime},
|
||||
|
|
@ -302,79 +300,29 @@ async fn main() -> anyhow::Result<()> {
|
|||
for (account_hash, campaign_id) in &legacy_auth_state.session_campaigns {
|
||||
app_sessions.migrate_preference(account_hash, *campaign_id)?;
|
||||
}
|
||||
let provider_name = std::env::var("GHOSTLIGHT_MODEL_PROVIDER")
|
||||
.unwrap_or_else(|_| "deepseek".into())
|
||||
.to_ascii_lowercase();
|
||||
let (default_fast_model, default_capable_model, default_secret_name) =
|
||||
match provider_name.as_str() {
|
||||
"deepseek" => ("deepseek-v4-flash", "deepseek-v4-pro", "deepseek.dpapi"),
|
||||
"openrouter" => ("stealth/ox-alpha", "stealth/ox-alpha", "openrouter.key"),
|
||||
"codex-connector" => ("gpt-5.6-luna", "gpt-5.6-luna", "codex-connector.key"),
|
||||
unsupported => anyhow::bail!("unsupported model provider {unsupported}"),
|
||||
let model_selection = ModelRuntimeSelection::from_environment(&runtime_root)?;
|
||||
let (model_status, compiler, assessor, shared_model) =
|
||||
if let Some(provider) = model_selection.open()? {
|
||||
let vault_endpoint = std::env::var("GHOSTLIGHT_VAULT_MCP_URL")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:17875/mcp".into());
|
||||
(
|
||||
model_selection.status("configured"),
|
||||
Some(Arc::new(WorldCompiler::new(
|
||||
Arc::new(VoidBotMcpVault::new(vault_endpoint)),
|
||||
provider.clone(),
|
||||
MODEL_FAST,
|
||||
MODEL_CAPABLE,
|
||||
))),
|
||||
Some(Arc::new(ActionAssessor::with_models(
|
||||
provider.clone(),
|
||||
MODEL_FAST,
|
||||
MODEL_CAPABLE,
|
||||
))),
|
||||
Some(provider),
|
||||
)
|
||||
} else {
|
||||
(model_selection.status("missing-secret"), None, None, None)
|
||||
};
|
||||
let fast_model =
|
||||
std::env::var("GHOSTLIGHT_MODEL_FAST").unwrap_or_else(|_| default_fast_model.into());
|
||||
let capable_model =
|
||||
std::env::var("GHOSTLIGHT_MODEL_CAPABLE").unwrap_or_else(|_| default_capable_model.into());
|
||||
let connector_max_concurrent_requests =
|
||||
std::env::var("GHOSTLIGHT_CODEX_CONNECTOR_MAX_CONCURRENT_REQUESTS")
|
||||
.unwrap_or_else(|_| "8".to_string())
|
||||
.parse::<usize>()
|
||||
.context("GHOSTLIGHT_CODEX_CONNECTOR_MAX_CONCURRENT_REQUESTS must be an integer")?;
|
||||
let secret_path = std::env::var_os("GHOSTLIGHT_MODEL_CREDENTIAL")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| runtime_root.join("secrets").join(default_secret_name));
|
||||
let configured_status = |readiness: String| ModelRuntimeStatus {
|
||||
provider: provider_name.clone(),
|
||||
fast_model: fast_model.clone(),
|
||||
capable_model: capable_model.clone(),
|
||||
readiness,
|
||||
};
|
||||
let (model_status, compiler, assessor, shared_model) = if secret_path.is_file() {
|
||||
let provider: Arc<dyn ModelPort> = match provider_name.as_str() {
|
||||
"deepseek" => Arc::new(DeepSeekPort::from_runtime_secret_with_models(
|
||||
&secret_path,
|
||||
fast_model.clone(),
|
||||
capable_model.clone(),
|
||||
)?),
|
||||
"openrouter" => Arc::new(OpenRouterPort::from_runtime_secret(
|
||||
&secret_path,
|
||||
fast_model.clone(),
|
||||
capable_model.clone(),
|
||||
)?),
|
||||
"codex-connector" => Arc::new(CodexConnectorModelPort::from_runtime_secret(
|
||||
std::env::var("GHOSTLIGHT_MODEL_CONNECTOR")
|
||||
.unwrap_or_else(|_| "127.0.0.1:4103".to_string())
|
||||
.parse()?,
|
||||
&secret_path,
|
||||
std::env::var("GHOSTLIGHT_RUNTIME_ID")
|
||||
.unwrap_or_else(|_| "ghostlight-dungeon-yggdrasil".to_string()),
|
||||
fast_model.clone(),
|
||||
capable_model.clone(),
|
||||
connector_max_concurrent_requests,
|
||||
)?),
|
||||
_ => unreachable!("provider name was validated above"),
|
||||
};
|
||||
let vault_endpoint = std::env::var("GHOSTLIGHT_VAULT_MCP_URL")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:17875/mcp".into());
|
||||
(
|
||||
configured_status("configured".into()),
|
||||
Some(Arc::new(WorldCompiler::new(
|
||||
Arc::new(VoidBotMcpVault::new(vault_endpoint)),
|
||||
provider.clone(),
|
||||
MODEL_FAST,
|
||||
MODEL_CAPABLE,
|
||||
))),
|
||||
Some(Arc::new(ActionAssessor::with_models(
|
||||
provider.clone(),
|
||||
MODEL_FAST,
|
||||
MODEL_CAPABLE,
|
||||
))),
|
||||
Some(provider),
|
||||
)
|
||||
} else {
|
||||
(configured_status("missing-secret".into()), None, None, None)
|
||||
};
|
||||
let mesh_target = std::env::var("GHOSTLIGHT_ODIN_RUDP")
|
||||
.ok()
|
||||
.map(|value| value.parse())
|
||||
|
|
|
|||
100
crates/ghostlight-dungeon/src/model_runtime.rs
Normal file
100
crates/ghostlight-dungeon/src/model_runtime.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::{
|
||||
model::{DeepSeekPort, ModelPort, ModelRuntimeStatus, OpenRouterPort},
|
||||
model_connector::CodexConnectorModelPort,
|
||||
};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ModelRuntimeSelection {
|
||||
pub provider: String,
|
||||
pub fast_model: String,
|
||||
pub capable_model: String,
|
||||
pub credential_path: PathBuf,
|
||||
pub connector_endpoint: SocketAddr,
|
||||
pub runtime_id: String,
|
||||
pub connector_max_concurrent_requests: usize,
|
||||
}
|
||||
|
||||
impl ModelRuntimeSelection {
|
||||
pub fn from_environment(runtime_root: impl AsRef<Path>) -> Result<Self> {
|
||||
let provider = std::env::var("GHOSTLIGHT_MODEL_PROVIDER")
|
||||
.unwrap_or_else(|_| "deepseek".into())
|
||||
.to_ascii_lowercase();
|
||||
let (default_fast_model, default_capable_model, default_secret_name) =
|
||||
match provider.as_str() {
|
||||
"deepseek" => ("deepseek-v4-flash", "deepseek-v4-pro", "deepseek.dpapi"),
|
||||
"openrouter" => ("stealth/ox-alpha", "stealth/ox-alpha", "openrouter.key"),
|
||||
"codex-connector" => ("gpt-5.6-luna", "gpt-5.6-luna", "codex-connector.key"),
|
||||
unsupported => bail!("unsupported model provider {unsupported}"),
|
||||
};
|
||||
Ok(Self {
|
||||
provider,
|
||||
fast_model: std::env::var("GHOSTLIGHT_MODEL_FAST")
|
||||
.unwrap_or_else(|_| default_fast_model.into()),
|
||||
capable_model: std::env::var("GHOSTLIGHT_MODEL_CAPABLE")
|
||||
.unwrap_or_else(|_| default_capable_model.into()),
|
||||
credential_path: std::env::var_os("GHOSTLIGHT_MODEL_CREDENTIAL")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| {
|
||||
runtime_root
|
||||
.as_ref()
|
||||
.join("secrets")
|
||||
.join(default_secret_name)
|
||||
}),
|
||||
connector_endpoint: std::env::var("GHOSTLIGHT_MODEL_CONNECTOR")
|
||||
.unwrap_or_else(|_| "127.0.0.1:4103".to_string())
|
||||
.parse()
|
||||
.context("GHOSTLIGHT_MODEL_CONNECTOR must be a socket address")?,
|
||||
runtime_id: std::env::var("GHOSTLIGHT_RUNTIME_ID")
|
||||
.unwrap_or_else(|_| "ghostlight-dungeon-yggdrasil".to_string()),
|
||||
connector_max_concurrent_requests: std::env::var(
|
||||
"GHOSTLIGHT_CODEX_CONNECTOR_MAX_CONCURRENT_REQUESTS",
|
||||
)
|
||||
.unwrap_or_else(|_| "8".to_string())
|
||||
.parse::<usize>()
|
||||
.context("GHOSTLIGHT_CODEX_CONNECTOR_MAX_CONCURRENT_REQUESTS must be an integer")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn status(&self, readiness: impl Into<String>) -> ModelRuntimeStatus {
|
||||
ModelRuntimeStatus {
|
||||
provider: self.provider.clone(),
|
||||
fast_model: self.fast_model.clone(),
|
||||
capable_model: self.capable_model.clone(),
|
||||
readiness: readiness.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&self) -> Result<Option<Arc<dyn ModelPort>>> {
|
||||
if !self.credential_path.is_file() {
|
||||
return Ok(None);
|
||||
}
|
||||
let provider: Arc<dyn ModelPort> = match self.provider.as_str() {
|
||||
"deepseek" => Arc::new(DeepSeekPort::from_runtime_secret_with_models(
|
||||
&self.credential_path,
|
||||
self.fast_model.clone(),
|
||||
self.capable_model.clone(),
|
||||
)?),
|
||||
"openrouter" => Arc::new(OpenRouterPort::from_runtime_secret(
|
||||
&self.credential_path,
|
||||
self.fast_model.clone(),
|
||||
self.capable_model.clone(),
|
||||
)?),
|
||||
"codex-connector" => Arc::new(CodexConnectorModelPort::from_runtime_secret(
|
||||
self.connector_endpoint,
|
||||
&self.credential_path,
|
||||
self.runtime_id.clone(),
|
||||
self.fast_model.clone(),
|
||||
self.capable_model.clone(),
|
||||
self.connector_max_concurrent_requests,
|
||||
)?),
|
||||
unsupported => bail!("unsupported model provider {unsupported}"),
|
||||
};
|
||||
Ok(Some(provider))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue