mirror of
https://github.com/whit3rabbit/anyllm-proxy.git
synced 2026-09-22 00:00:50 +00:00
Introduce `anyllm_client` crate containing HTTP client construction, SSRF-safe DNS resolver, retry/backoff logic, rate limit header parsing, and SSE frame parsing. These were previously inlined in the proxy crate. Rename crates from `anthropic_openai_proxy`/`anthropic_openai_translate` to `anyllm_proxy`/`anyllm_translate` throughout. proxy/backend: now re-exports retry, rate limit, and SSE symbols from the client crate; `send_with_retry` and `build_http_client` are thin adapters bridging BackendAuth/TlsConfig to the client crate's types. streaming: remove duplicate `find_double_newline` and `MAX_SSE_BUFFER_SIZE` definitions; import from `crate::backend` instead. Fix missing `pub mod` declarations in translator and proxy that were accidentally replaced by doc comments (streaming, usage_map, server, redact). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
95 lines
2.7 KiB
Rust
95 lines
2.7 KiB
Rust
//! Integration test: verify the crate works as a standalone library without the proxy.
|
|
|
|
use anyllm_translate::anthropic::{MessageCreateRequest, MessageResponse, Usage};
|
|
use anyllm_translate::openai::ChatCompletionResponse;
|
|
use anyllm_translate::{translate_request, translate_response, TranslateError, TranslationConfig};
|
|
|
|
#[test]
|
|
fn standalone_translate_request() {
|
|
let config = TranslationConfig::builder()
|
|
.model_map("haiku", "gpt-4o-mini")
|
|
.model_map("sonnet", "gpt-4o")
|
|
.build();
|
|
|
|
let req: MessageCreateRequest = serde_json::from_str(
|
|
r#"{
|
|
"model": "claude-sonnet-4-6",
|
|
"max_tokens": 100,
|
|
"messages": [{"role": "user", "content": "Hello"}]
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let openai_req = translate_request(&req, &config).unwrap();
|
|
assert_eq!(openai_req.model, "gpt-4o");
|
|
assert_eq!(openai_req.max_completion_tokens, Some(100));
|
|
}
|
|
|
|
#[test]
|
|
fn standalone_translate_response() {
|
|
let openai_resp: ChatCompletionResponse = serde_json::from_str(
|
|
r#"{
|
|
"id": "chatcmpl-abc",
|
|
"object": "chat.completion",
|
|
"created": 1700000000,
|
|
"model": "gpt-4o",
|
|
"choices": [{
|
|
"index": 0,
|
|
"message": {"role": "assistant", "content": "Hello!"},
|
|
"finish_reason": "stop"
|
|
}],
|
|
"usage": {"prompt_tokens": 5, "completion_tokens": 2, "total_tokens": 7}
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let resp: MessageResponse = translate_response(&openai_resp, "claude-sonnet-4-6");
|
|
assert_eq!(resp.model, "claude-sonnet-4-6");
|
|
assert_eq!(
|
|
resp.usage,
|
|
Usage {
|
|
input_tokens: 5,
|
|
output_tokens: 2,
|
|
..Default::default()
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn standalone_strict_mode_rejects_unknown() {
|
|
let config = TranslationConfig::builder()
|
|
.model_map("sonnet", "gpt-4o")
|
|
.passthrough_unknown_models(false)
|
|
.build();
|
|
|
|
let req: MessageCreateRequest = serde_json::from_str(
|
|
r#"{
|
|
"model": "unknown-model",
|
|
"max_tokens": 10,
|
|
"messages": [{"role": "user", "content": "Hi"}]
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let err = translate_request(&req, &config).unwrap_err();
|
|
assert!(matches!(err, TranslateError::UnknownModel(_)));
|
|
assert!(err.to_string().contains("unknown-model"));
|
|
}
|
|
|
|
#[test]
|
|
fn default_config_passthrough() {
|
|
let config = TranslationConfig::default();
|
|
|
|
let req: MessageCreateRequest = serde_json::from_str(
|
|
r#"{
|
|
"model": "any-model-name",
|
|
"max_tokens": 10,
|
|
"messages": [{"role": "user", "content": "test"}]
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
let openai_req = translate_request(&req, &config).unwrap();
|
|
assert_eq!(openai_req.model, "any-model-name");
|
|
}
|