refactor: cortex talks to neurons instead of mistral.rs directly
Replace NodeConfig (static vram_mb, pinned) with NeuronEndpoint.
Hardware discovery and model pinning now come from neuron API and
models.toml catalogue respectively.
- config.rs: nodes -> neurons, add models_config path
- catalogue.rs: ModelProfile with pinned_on, ModelCatalogue
- poller.rs: poll neuron GET /models (ModelInfo format)
- router.rs: resolve inference endpoint via neuron GET /models/{id}/endpoint
- evictor.rs: call neuron POST /models/unload
- node.rs: remove vram_mb, pinned fields (come from discovery/catalogue)
- All 22 gateway tests updated to mock neuron API
- Remove MistralModelsResponse, ModelLifecycleRequest (no longer needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_anthropic_to_openai_round_trip() {
|
||||
let mock_url = common::spawn_mock_backend().await;
|
||||
let mock_url = common::spawn_mock_neuron().await;
|
||||
let gw_url = common::spawn_gateway(&mock_url).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
@@ -14,9 +14,7 @@ async fn test_anthropic_to_openai_round_trip() {
|
||||
.json(&json!({
|
||||
"model": "test-model",
|
||||
"max_tokens": 100,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hi"}
|
||||
]
|
||||
"messages": [{"role": "user", "content": "Hi"}]
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
@@ -25,29 +23,22 @@ async fn test_anthropic_to_openai_round_trip() {
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let body: serde_json::Value = resp.json().await.expect("valid JSON");
|
||||
|
||||
// Response should be in Anthropic format.
|
||||
assert_eq!(body["type"], "message");
|
||||
assert_eq!(body["role"], "assistant");
|
||||
assert_eq!(body["model"], "test-model");
|
||||
|
||||
// Content should be an array of content blocks.
|
||||
let content = body["content"].as_array().expect("content array");
|
||||
assert_eq!(content.len(), 1);
|
||||
assert_eq!(content[0]["type"], "text");
|
||||
assert_eq!(content[0]["text"], "Hello from mock backend");
|
||||
|
||||
// Stop reason should be translated from "stop" to "end_turn".
|
||||
assert_eq!(body["stop_reason"], "end_turn");
|
||||
|
||||
// Usage should have Anthropic field names.
|
||||
assert_eq!(body["usage"]["input_tokens"], 10);
|
||||
assert_eq!(body["usage"]["output_tokens"], 5);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_anthropic_with_system_prompt() {
|
||||
let mock_url = common::spawn_mock_backend().await;
|
||||
let mock_url = common::spawn_mock_neuron().await;
|
||||
let gw_url = common::spawn_gateway(&mock_url).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
@@ -58,24 +49,20 @@ async fn test_anthropic_with_system_prompt() {
|
||||
"model": "test-model",
|
||||
"max_tokens": 100,
|
||||
"system": "You are a helpful assistant.",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hi"}
|
||||
]
|
||||
"messages": [{"role": "user", "content": "Hi"}]
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let body: serde_json::Value = resp.json().await.expect("valid JSON");
|
||||
assert_eq!(body["type"], "message");
|
||||
assert_eq!(body["content"][0]["text"], "Hello from mock backend");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_anthropic_with_content_blocks() {
|
||||
let mock_url = common::spawn_mock_backend().await;
|
||||
let mock_url = common::spawn_mock_neuron().await;
|
||||
let gw_url = common::spawn_gateway(&mock_url).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
@@ -85,29 +72,23 @@ async fn test_anthropic_with_content_blocks() {
|
||||
.json(&json!({
|
||||
"model": "test-model",
|
||||
"max_tokens": 100,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "What is this?"}
|
||||
]
|
||||
}
|
||||
]
|
||||
"messages": [{
|
||||
"role": "user",
|
||||
"content": [{"type": "text", "text": "What is this?"}]
|
||||
}]
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let body: serde_json::Value = resp.json().await.expect("valid JSON");
|
||||
assert_eq!(body["type"], "message");
|
||||
assert_eq!(body["content"][0]["text"], "Hello from mock backend");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_anthropic_model_not_found() {
|
||||
let mock_url = common::spawn_mock_backend().await;
|
||||
let mock_url = common::spawn_mock_neuron().await;
|
||||
let gw_url = common::spawn_gateway(&mock_url).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
@@ -117,9 +98,7 @@ async fn test_anthropic_model_not_found() {
|
||||
.json(&json!({
|
||||
"model": "nonexistent",
|
||||
"max_tokens": 100,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hi"}
|
||||
]
|
||||
"messages": [{"role": "user", "content": "Hi"}]
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
@@ -130,27 +109,17 @@ async fn test_anthropic_model_not_found() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_anthropic_invalid_request() {
|
||||
let mock_url = common::spawn_mock_backend().await;
|
||||
let mock_url = common::spawn_mock_neuron().await;
|
||||
let gw_url = common::spawn_gateway(&mock_url).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.post(format!("{gw_url}/v1/messages"))
|
||||
.header("content-type", "application/json")
|
||||
.json(&json!({
|
||||
"not_a_valid": "request"
|
||||
}))
|
||||
.json(&json!({"not_a_valid": "request"}))
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
let body: serde_json::Value = resp.json().await.unwrap();
|
||||
assert!(
|
||||
body["error"]["message"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("invalid Anthropic request")
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user