Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 0 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,3 @@ unsafe_code = "forbid"
[workspace.lints.clippy]
all = { level = "deny", priority = -1 }
pedantic = { level = "warn", priority = -1 }

[workspace.dependencies]
agentic-core = { path = "crates/agentic-core" }
axum = "0.8"
bytes = "1"
clap = { version = "4", features = ["derive", "env"] }
criterion = { version = "0.5", features = ["async_tokio"] }
futures = "0.3"
http = "1"
reqwest = { version = "0.12", features = ["stream"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
tower-http = { version = "0.6", features = ["cors"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
9 changes: 9 additions & 0 deletions crates/agentic-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ license.workspace = true
repository.workspace = true

[dependencies]
bytes = "1"
futures = "0.3"
http = "1"
reqwest = { version = "0.12", features = ["stream"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
tokio = { version = "1", features = ["time"] }
tracing = "0.1"

[lints]
workspace = true
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[derive(Debug, Clone)]
pub struct GatewayConfig {
pub struct Config {
pub llm_api_base: String,
pub openai_api_key: Option<String>,
pub llm_ready_timeout_s: f64,
Expand Down
File renamed without changes.
5 changes: 4 additions & 1 deletion crates/agentic-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
// Framework-agnostic core abstractions (store traits, execution logic) are added by follow-up PRs.
pub mod config;
pub mod error;
pub mod proxy;
pub mod readiness;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest::Client;
use serde_json::Value;
use tracing::warn;

use crate::config::GatewayConfig;
use crate::config::Config;
use crate::error::Error;

const HOP_BY_HOP: &[&str] = &[
Expand Down Expand Up @@ -51,7 +51,7 @@ pub struct ProxyResponse {

#[derive(Clone)]
pub struct ProxyState {
pub config: GatewayConfig,
pub config: Config,
pub stream_client: Client,
pub non_stream_client: Client,
}
Expand All @@ -60,7 +60,7 @@ impl ProxyState {
/// # Errors
///
/// Returns an error if the HTTP clients cannot be built.
pub fn new(config: GatewayConfig) -> Result<Self, Error> {
pub fn new(config: Config) -> Result<Self, Error> {
let stream_client = Client::builder()
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(900))
Expand All @@ -84,7 +84,7 @@ impl ProxyState {
}
}

fn filter_request_headers(headers: &HeaderMap, config: &GatewayConfig) -> reqwest::header::HeaderMap {
fn filter_request_headers(headers: &HeaderMap, config: &Config) -> reqwest::header::HeaderMap {
let mut out = reqwest::header::HeaderMap::new();
for (name, value) in headers {
if is_request_drop(name.as_str()) {
Expand Down Expand Up @@ -223,19 +223,19 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe
#[cfg(test)]
mod tests {
use super::*;
use crate::config::GatewayConfig;
use crate::config::Config;

fn test_config() -> GatewayConfig {
GatewayConfig {
fn test_config() -> Config {
Config {
llm_api_base: "http://localhost:8000".to_owned(),
openai_api_key: Some("test-key".to_owned()),
llm_ready_timeout_s: 5.0,
llm_ready_interval_s: 0.1,
}
}

fn test_config_no_key() -> GatewayConfig {
GatewayConfig {
fn test_config_no_key() -> Config {
Config {
openai_api_key: None,
..test_config()
}
Expand Down Expand Up @@ -326,7 +326,7 @@ mod tests {
#[test]
fn no_auth_injected_when_key_empty() {
let headers = HeaderMap::new();
let config = GatewayConfig {
let config = Config {
openai_api_key: Some(" ".to_owned()),
..test_config()
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::time::Duration;

use tracing::info;

use crate::config::GatewayConfig;
use crate::config::Config;
use crate::error::Error;

fn checked_duration_seconds(name: &str, value: f64) -> Result<Duration, Error> {
Expand All @@ -27,7 +27,7 @@ fn timeout_error(url: &str, timeout_s: f64) -> Error {
/// # Errors
///
/// Returns an error if the LLM does not become ready within the configured timeout.
pub async fn wait_llm_ready(config: &GatewayConfig) -> Result<(), Error> {
pub async fn wait_llm_ready(config: &Config) -> Result<(), Error> {
let base = config.llm_api_base.trim_end_matches('/');
let url = format!("{base}/health");

Expand Down
2 changes: 1 addition & 1 deletion crates/agentic-praxis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license.workspace = true
repository.workspace = true

[dependencies]
agentic-core.workspace = true
agentic-core = { path = "../agentic-core" }

[lints]
workspace = true
32 changes: 16 additions & 16 deletions crates/agentic-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@ license.workspace = true
repository.workspace = true

[dependencies]
agentic-core.workspace = true
axum.workspace = true
bytes.workspace = true
clap.workspace = true
futures.workspace = true
http.workspace = true
reqwest = { workspace = true, features = ["json"] }
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio.workspace = true
tower-http.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
agentic-core = { path = "../agentic-core" }
axum = "0.8"
clap = { version = "4", features = ["derive", "env"] }
http = "1"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
tower-http = { version = "0.6", features = ["cors"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[dev-dependencies]
criterion.workspace = true
tokio = { workspace = true, features = ["test-util"] }
bytes = "1"
criterion = { version = "0.5", features = ["async_tokio"] }
futures = "0.3"
reqwest = { version = "0.12", features = ["json", "stream"] }
serde_json = "1"
tokio = { version = "1", features = ["full", "test-util"] }

[[bench]]
name = "proxy_bench"
Expand Down
10 changes: 5 additions & 5 deletions crates/agentic-server/benches/proxy_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ use http::StatusCode;
use tokio::net::TcpListener;
use tokio::runtime::Runtime;

use agentic_core::config::Config;
use agentic_core::proxy::ProxyState;
use agentic_server::app::build_router;
use agentic_server::config::GatewayConfig;
use agentic_server::proxy::ProxyState;

fn bench_config(llm_url: &str) -> GatewayConfig {
GatewayConfig {
fn bench_config(llm_url: &str) -> Config {
Config {
llm_api_base: llm_url.to_owned(),
openai_api_key: Some("bench-key".to_owned()),
llm_ready_timeout_s: 5.0,
Expand Down Expand Up @@ -70,7 +70,7 @@ async fn spawn_llm() -> String {
format!("http://{addr}")
}

async fn spawn_gateway(config: GatewayConfig) -> String {
async fn spawn_gateway(config: Config) -> String {
let state = ProxyState::new(config).unwrap();
let router = build_router(state);

Expand Down
2 changes: 1 addition & 1 deletion crates/agentic-server/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use agentic_core::proxy::ProxyState;
use axum::Router;
use axum::routing::{get, post};

use crate::handler::{health, proxy_responses, ready};
use crate::proxy::ProxyState;

pub fn build_router(state: ProxyState) -> Router {
Router::new()
Expand Down
5 changes: 2 additions & 3 deletions crates/agentic-server/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use agentic_core::proxy::{ProxyBody, ProxyRequest, ProxyResponse, ProxyState, error_response};
use axum::body::Body;
use axum::extract::State;
use axum::response::{IntoResponse, Response};
use http::StatusCode;
use tracing::warn;

use crate::proxy::{ProxyBody, ProxyRequest, ProxyResponse, ProxyState, error_response};

const MAX_BODY_SIZE: usize = 10 * 1024 * 1024;

pub async fn health() -> impl IntoResponse {
Expand Down Expand Up @@ -76,5 +75,5 @@ pub async fn proxy_responses(State(state): State<ProxyState>, req: axum::extract
query: parts.uri.query().map(String::from),
};

convert_response(crate::proxy::proxy_request(proxy_req, &state).await)
convert_response(agentic_core::proxy::proxy_request(proxy_req, &state).await)
}
4 changes: 0 additions & 4 deletions crates/agentic-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
pub mod app;
pub mod config;
pub mod error;
pub mod handler;
pub mod proxy;
pub mod readiness;
8 changes: 4 additions & 4 deletions crates/agentic-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::{Args, Parser, Subcommand};

use agentic_server::config::{GatewayConfig, normalize_base_url};
use agentic_server::error::Error;
use agentic_core::config::{Config, normalize_base_url};
use agentic_core::error::Error;

mod server;

Expand Down Expand Up @@ -53,8 +53,8 @@ enum Commands {
},
}

fn build_config(llm_api_base: String, common: &CommonArgs) -> GatewayConfig {
GatewayConfig {
fn build_config(llm_api_base: String, common: &CommonArgs) -> Config {
Config {
llm_api_base,
openai_api_key: common.openai_api_key.clone(),
llm_ready_timeout_s: common.llm_ready_timeout_s,
Expand Down
14 changes: 7 additions & 7 deletions crates/agentic-server/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use agentic_core::config::Config;
use agentic_core::error::Error;
use agentic_core::proxy::ProxyState;
use agentic_core::readiness::wait_llm_ready;
use agentic_server::app::build_router;
use agentic_server::config::GatewayConfig;
use agentic_server::error::Error;
use agentic_server::proxy::ProxyState;
use agentic_server::readiness::wait_llm_ready;
use tokio::net::TcpListener;
use tracing::info;

async fn serve_gateway(config: GatewayConfig, host: &str, port: u16) -> Result<(), Error> {
async fn serve_gateway(config: Config, host: &str, port: u16) -> Result<(), Error> {
let addr = format!("{host}:{port}");
let state = ProxyState::new(config)?;
let router = build_router(state);
Expand All @@ -21,7 +21,7 @@ async fn serve_gateway(config: GatewayConfig, host: &str, port: u16) -> Result<(
/// # Errors
///
/// Returns an error if LLM readiness polling fails or the server cannot bind.
pub async fn run(config: GatewayConfig, host: &str, port: u16) -> Result<(), Error> {
pub async fn run(config: Config, host: &str, port: u16) -> Result<(), Error> {
wait_llm_ready(&config).await?;
info!("LLM ready: {}", config.llm_api_base);
serve_gateway(config, host, port).await
Expand All @@ -32,7 +32,7 @@ pub async fn run(config: GatewayConfig, host: &str, port: u16) -> Result<(), Err
/// # Errors
///
/// Returns an error if vLLM fails to start or the gateway errors.
pub async fn run_with_llm(config: GatewayConfig, host: &str, port: u16, llm_args: Vec<String>) -> Result<(), Error> {
pub async fn run_with_llm(config: Config, host: &str, port: u16, llm_args: Vec<String>) -> Result<(), Error> {
let mut cmd = tokio::process::Command::new("python");
cmd.arg("-m").arg("vllm.entrypoints.openai.api_server");
cmd.args(&llm_args);
Expand Down
14 changes: 7 additions & 7 deletions crates/agentic-server/tests/health_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ use axum::routing::get;
use http::StatusCode;
use tokio::net::TcpListener;

use agentic_server::config::GatewayConfig;
use agentic_server::proxy::ProxyState;
use agentic_core::config::Config;
use agentic_core::proxy::ProxyState;

fn test_config(llm_url: &str) -> GatewayConfig {
GatewayConfig {
fn test_config(llm_url: &str) -> Config {
Config {
llm_api_base: llm_url.to_owned(),
openai_api_key: Some("env-llm-key".to_owned()),
llm_ready_timeout_s: 5.0,
llm_ready_interval_s: 0.1,
}
}

fn test_config_no_key(llm_url: &str) -> GatewayConfig {
GatewayConfig {
fn test_config_no_key(llm_url: &str) -> Config {
Config {
openai_api_key: None,
..test_config(llm_url)
}
Expand All @@ -33,7 +33,7 @@ async fn spawn_mock_llm() -> (String, tokio::task::JoinHandle<()>) {
(format!("http://{addr}"), handle)
}

async fn spawn_gateway(config: GatewayConfig) -> (String, tokio::task::JoinHandle<()>) {
async fn spawn_gateway(config: Config) -> (String, tokio::task::JoinHandle<()>) {
let state = ProxyState::new(config).unwrap();
let router = agentic_server::app::build_router(state);
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
Expand Down