gproxy-protocol 4.0.3

GPROXY v4 connection modelling, operation declarations, vendor wire types and pairwise conversion
Documentation
use super::*;
use gproxy_protocol::adapt::generate::{
    claude_gemini::GeminiViaClaude, claude_responses::ResponsesViaClaude,
};

fn target(source: Dialect, _model: &str) -> StreamTarget {
    let namespace = match source {
        Dialect::OpenAiChat => 61,
        Dialect::OpenAi => 63,
        Dialect::Gemini => 65,
        _ => unreachable!(),
    };
    StreamTarget {
        endpoint: Endpoint::new("/messages").unwrap(),
        identities: GenerationIdentity::new(
            IdNamespace([namespace; 16]),
            IdNamespace([namespace + 1; 16]),
            source,
            Dialect::Claude,
        )
        .unwrap(),
    }
}

#[test]
fn all_three_stream_preparations_preserve_instruction_positions() {
    for model in ["claude-opus-4-8", "claude-sonnet-5"] {
        for preceding in ["user", "assistant"] {
            let store = Store::default();
            let mut access = state(&store);
            access.target = IdentityTarget::new(model, Dialect::Claude)
                .unwrap()
                .with_origin("origin")
                .unwrap();
            let messages = json!([
                {"role":"user","content":"A"},
                {"role":preceding,"content":"B"},
                {"role":"system","content":"从现在起只用中文"},
                {"role":"developer","content":"保持简短"}
            ]);
            let chat = ready(ChatViaClaude::prepare_stream(
                serde_json::from_value(json!({"model":"client-alias","stream":true,"max_completion_tokens":32,"messages":messages})).unwrap(),
                target(Dialect::OpenAiChat, model), ClaudeToChatContext { created: 7 }, settings(), &access,
            )).unwrap();
            let responses = ready(ResponsesViaClaude::prepare_stream(
                serde_json::from_value(json!({"model":"client-alias","stream":true,"max_output_tokens":32,"input":messages})).unwrap(),
                target(Dialect::OpenAi, model), ResponsesViaClaudeStreamFacts {
                    request: Default::default(), response: all_pairs::claude_response_context(),
                }, settings(), &access,
            )).unwrap();
            let gemini = ready(GeminiViaClaude::prepare_stream(
                serde_json::from_value(json!({"generationConfig":{"maxOutputTokens":32},"contents":[
                    {"role":"user","parts":[{"text":"A"}]},
                    {"role":if preceding == "assistant" { "model" } else { "user" },"parts":[{"text":"B"}]},
                    {"role":"system","parts":[{"text":"从现在起只用中文"}]},
                    {"role":"system","parts":[{"text":"保持简短"}]}
                ]})).unwrap(),
                target(Dialect::Gemini, model), GeminiViaClaudeStreamFacts { max_tokens: None, response: Default::default() }, settings(), &access,
            )).unwrap();
            let expected = if model == "claude-opus-4-8" && preceding == "user" {
                "system"
            } else {
                "user"
            };
            for request in [
                chat.target_request(),
                responses.target_request(),
                gemini.target_request(),
            ] {
                let wire = serde_json::to_value(request).unwrap();
                assert!(wire.get("system").is_none());
                assert_eq!(wire["model"], model);
                assert_eq!(wire["stream"], true);
                let roles: Vec<_> = wire["messages"]
                    .as_array()
                    .unwrap()
                    .iter()
                    .map(|message| message["role"].as_str().unwrap())
                    .collect();
                assert_eq!(roles, ["user", preceding, expected, expected]);
            }
        }
    }
}