This release represents a comprehensive transformation of the codebase from Codex to LLMX, enhanced with LiteLLM integration to support 100+ LLM providers through a unified API. ## Major Changes ### Phase 1: Repository & Infrastructure Setup - Established new repository structure and branching strategy - Created comprehensive project documentation (CLAUDE.md, LITELLM-SETUP.md) - Set up development environment and tooling configuration ### Phase 2: Rust Workspace Transformation - Renamed all Rust crates from `codex-*` to `llmx-*` (30+ crates) - Updated package names, binary names, and workspace members - Renamed core modules: codex.rs → llmx.rs, codex_delegate.rs → llmx_delegate.rs - Updated all internal references, imports, and type names - Renamed directories: codex-rs/ → llmx-rs/, codex-backend-openapi-models/ → llmx-backend-openapi-models/ - Fixed all Rust compilation errors after mass rename ### Phase 3: LiteLLM Integration - Integrated LiteLLM for multi-provider LLM support (Anthropic, OpenAI, Azure, Google AI, AWS Bedrock, etc.) - Implemented OpenAI-compatible Chat Completions API support - Added model family detection and provider-specific handling - Updated authentication to support LiteLLM API keys - Renamed environment variables: OPENAI_BASE_URL → LLMX_BASE_URL - Added LLMX_API_KEY for unified authentication - Enhanced error handling for Chat Completions API responses - Implemented fallback mechanisms between Responses API and Chat Completions API ### Phase 4: TypeScript/Node.js Components - Renamed npm package: @codex/codex-cli → @valknar/llmx - Updated TypeScript SDK to use new LLMX APIs and endpoints - Fixed all TypeScript compilation and linting errors - Updated SDK tests to support both API backends - Enhanced mock server to handle multiple API formats - Updated build scripts for cross-platform packaging ### Phase 5: Configuration & Documentation - Updated all configuration files to use LLMX naming - Rewrote README and documentation for LLMX branding - Updated config paths: ~/.codex/ → ~/.llmx/ - Added comprehensive LiteLLM setup guide - Updated all user-facing strings and help text - Created release plan and migration documentation ### Phase 6: Testing & Validation - Fixed all Rust tests for new naming scheme - Updated snapshot tests in TUI (36 frame files) - Fixed authentication storage tests - Updated Chat Completions payload and SSE tests - Fixed SDK tests for new API endpoints - Ensured compatibility with Claude Sonnet 4.5 model - Fixed test environment variables (LLMX_API_KEY, LLMX_BASE_URL) ### Phase 7: Build & Release Pipeline - Updated GitHub Actions workflows for LLMX binary names - Fixed rust-release.yml to reference llmx-rs/ instead of codex-rs/ - Updated CI/CD pipelines for new package names - Made Apple code signing optional in release workflow - Enhanced npm packaging resilience for partial platform builds - Added Windows sandbox support to workspace - Updated dotslash configuration for new binary names ### Phase 8: Final Polish - Renamed all assets (.github images, labels, templates) - Updated VSCode and DevContainer configurations - Fixed all clippy warnings and formatting issues - Applied cargo fmt and prettier formatting across codebase - Updated issue templates and pull request templates - Fixed all remaining UI text references ## Technical Details **Breaking Changes:** - Binary name changed from `codex` to `llmx` - Config directory changed from `~/.codex/` to `~/.llmx/` - Environment variables renamed (CODEX_* → LLMX_*) - npm package renamed to `@valknar/llmx` **New Features:** - Support for 100+ LLM providers via LiteLLM - Unified authentication with LLMX_API_KEY - Enhanced model provider detection and handling - Improved error handling and fallback mechanisms **Files Changed:** - 578 files modified across Rust, TypeScript, and documentation - 30+ Rust crates renamed and updated - Complete rebrand of UI, CLI, and documentation - All tests updated and passing **Dependencies:** - Updated Cargo.lock with new package names - Updated npm dependencies in llmx-cli - Enhanced OpenAPI models for LLMX backend This release establishes LLMX as a standalone project with comprehensive LiteLLM integration, maintaining full backward compatibility with existing functionality while opening support for a wide ecosystem of LLM providers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Sebastian Krüger <support@pivoine.art>
106 lines
3.5 KiB
Rust
106 lines
3.5 KiB
Rust
#![cfg(not(target_os = "windows"))]
|
||
|
||
use core_test_support::responses;
|
||
use core_test_support::skip_if_no_network;
|
||
use core_test_support::test_llmx::TestLlmx;
|
||
use core_test_support::test_llmx::test_llmx;
|
||
use core_test_support::wait_for_event;
|
||
use llmx_core::protocol::AskForApproval;
|
||
use llmx_core::protocol::EventMsg;
|
||
use llmx_core::protocol::Op;
|
||
use llmx_core::protocol::SandboxPolicy;
|
||
use llmx_protocol::config_types::ReasoningSummary;
|
||
use llmx_protocol::user_input::UserInput;
|
||
use pretty_assertions::assert_eq;
|
||
use responses::ev_assistant_message;
|
||
use responses::ev_completed;
|
||
use responses::sse;
|
||
use responses::start_mock_server;
|
||
|
||
const SCHEMA: &str = r#"
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"explanation": { "type": "string" },
|
||
"final_answer": { "type": "string" }
|
||
},
|
||
"required": ["explanation", "final_answer"],
|
||
"additionalProperties": false
|
||
}
|
||
"#;
|
||
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||
async fn llmx_returns_json_result_for_gpt5() -> anyhow::Result<()> {
|
||
llmx_returns_json_result("gpt-5".to_string()).await
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||
async fn llmx_returns_json_result_for_gpt5_llmx() -> anyhow::Result<()> {
|
||
llmx_returns_json_result("gpt-5-llmx".to_string()).await
|
||
}
|
||
|
||
async fn llmx_returns_json_result(model: String) -> anyhow::Result<()> {
|
||
skip_if_no_network!(Ok(()));
|
||
|
||
let server = start_mock_server().await;
|
||
|
||
let sse1 = sse(vec![
|
||
ev_assistant_message(
|
||
"m2",
|
||
r#"{"explanation": "explanation", "final_answer": "final_answer"}"#,
|
||
),
|
||
ev_completed("r1"),
|
||
]);
|
||
|
||
let expected_schema: serde_json::Value = serde_json::from_str(SCHEMA)?;
|
||
let match_json_text_param = move |req: &wiremock::Request| {
|
||
let body: serde_json::Value = serde_json::from_slice(&req.body).unwrap_or_default();
|
||
let Some(text) = body.get("text") else {
|
||
return false;
|
||
};
|
||
let Some(format) = text.get("format") else {
|
||
return false;
|
||
};
|
||
|
||
format.get("name") == Some(&serde_json::Value::String("llmx_output_schema".into()))
|
||
&& format.get("type") == Some(&serde_json::Value::String("json_schema".into()))
|
||
&& format.get("strict") == Some(&serde_json::Value::Bool(true))
|
||
&& format.get("schema") == Some(&expected_schema)
|
||
};
|
||
responses::mount_sse_once_match(&server, match_json_text_param, sse1).await;
|
||
|
||
let TestLlmx { llmx, cwd, .. } = test_llmx().build(&server).await?;
|
||
|
||
// 1) Normal user input – should hit server once.
|
||
llmx.submit(Op::UserTurn {
|
||
items: vec![UserInput::Text {
|
||
text: "hello world".into(),
|
||
}],
|
||
final_output_json_schema: Some(serde_json::from_str(SCHEMA)?),
|
||
cwd: cwd.path().to_path_buf(),
|
||
approval_policy: AskForApproval::Never,
|
||
sandbox_policy: SandboxPolicy::DangerFullAccess,
|
||
model,
|
||
effort: None,
|
||
summary: ReasoningSummary::Auto,
|
||
})
|
||
.await?;
|
||
|
||
let message = wait_for_event(&llmx, |ev| matches!(ev, EventMsg::AgentMessage(_))).await;
|
||
if let EventMsg::AgentMessage(message) = message {
|
||
let json: serde_json::Value = serde_json::from_str(&message.message)?;
|
||
assert_eq!(
|
||
json.get("explanation"),
|
||
Some(&serde_json::Value::String("explanation".into()))
|
||
);
|
||
assert_eq!(
|
||
json.get("final_answer"),
|
||
Some(&serde_json::Value::String("final_answer".into()))
|
||
);
|
||
} else {
|
||
anyhow::bail!("expected agent message event");
|
||
}
|
||
|
||
Ok(())
|
||
}
|