mirror of
https://github.com/katanemo/plano.git
synced 2026-07-02 15:51:02 +02:00
more refactoring changes to avoid unecessary re-direction and duplication
This commit is contained in:
parent
58028bb7ae
commit
9c09a18fd0
12 changed files with 809 additions and 225 deletions
|
|
@ -46,18 +46,18 @@ mod tests {
|
|||
#[test]
|
||||
fn test_provider_instance_creation() {
|
||||
let provider = Provider::new(ProviderId::OpenAI);
|
||||
assert!(provider.interface().has_compatible_api("/v1/chat/completions"));
|
||||
assert!(!provider.interface().has_compatible_api("/v1/embeddings"));
|
||||
assert!(provider.has_compatible_api("/v1/chat/completions"));
|
||||
assert!(!provider.has_compatible_api("/v1/embeddings"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conversion_mode() {
|
||||
fn test_provider_supported_apis() {
|
||||
let provider = Provider::new(ProviderId::OpenAI);
|
||||
|
||||
let compatible_mode = provider.interface().get_interface(false);
|
||||
assert!(matches!(compatible_mode, ConversionMode::Compatible));
|
||||
let supported_apis = provider.supported_apis();
|
||||
assert!(supported_apis.contains(&"/v1/chat/completions"));
|
||||
|
||||
let passthrough_mode = provider.interface().get_interface(true);
|
||||
assert!(matches!(passthrough_mode, ConversionMode::Passthrough));
|
||||
// Test that provider supports the expected API endpoints
|
||||
assert!(provider.has_compatible_api("/v1/chat/completions"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,76 @@
|
|||
//! Arch provider implementation
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Arch provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArchProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for ArchProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for ArchProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for ArchProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for ArchProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
matches!(api_path, "/v1/chat/completions")
|
||||
|
|
@ -18,21 +81,21 @@ impl ProviderInterface for ArchProvider {
|
|||
}
|
||||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,76 @@
|
|||
//! For now, uses OpenAI-compatible format as fallback
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Claude provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClaudeProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for ClaudeProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for ClaudeProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for ClaudeProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for ClaudeProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
// TODO: Update when Claude API is fully implemented
|
||||
|
|
@ -24,7 +87,7 @@ impl ProviderInterface for ClaudeProvider {
|
|||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Claude-specific request parsing
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -32,7 +95,7 @@ impl ProviderInterface for ClaudeProvider {
|
|||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Claude-specific response parsing
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -40,7 +103,7 @@ impl ProviderInterface for ClaudeProvider {
|
|||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Claude-specific request serialization
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,76 @@
|
|||
//! Deepseek provider implementation
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Deepseek provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeepseekProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for DeepseekProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for DeepseekProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for DeepseekProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for DeepseekProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
matches!(api_path, "/v1/chat/completions")
|
||||
|
|
@ -18,21 +81,21 @@ impl ProviderInterface for DeepseekProvider {
|
|||
}
|
||||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,79 @@
|
|||
//! Gemini provider implementation
|
||||
//!
|
||||
//! TODO: Implement Gemini-specific API format when needed
|
||||
//! For now, uses OpenAI-compatible format as fallback
|
||||
//! This module contains the Gemini provider that handles Google's Gemini API format
|
||||
//! requests in OpenAI-compatible format.
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Gemini provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GeminiProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for GeminiProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for GeminiProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for GeminiProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for GeminiProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
// TODO: Update when Gemini API is fully implemented
|
||||
|
|
@ -24,7 +87,7 @@ impl ProviderInterface for GeminiProvider {
|
|||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Gemini-specific request parsing
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -32,7 +95,7 @@ impl ProviderInterface for GeminiProvider {
|
|||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Gemini-specific response parsing
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -40,7 +103,7 @@ impl ProviderInterface for GeminiProvider {
|
|||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement Gemini-specific request serialization
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,79 @@
|
|||
//! GitHub provider implementation
|
||||
//!
|
||||
//! TODO: Implement GitHub-specific API format (/models) when needed
|
||||
//! For now, uses OpenAI-compatible format as fallback
|
||||
//! This module contains the GitHub provider that handles GitHub API format
|
||||
//! requests in OpenAI-compatible format.
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// GitHub provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GitHubProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for GitHubProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for GitHubProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for GitHubProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for GitHubProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
// TODO: Update when GitHub API is fully implemented
|
||||
|
|
@ -24,7 +87,7 @@ impl ProviderInterface for GitHubProvider {
|
|||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement GitHub-specific request parsing
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -32,7 +95,7 @@ impl ProviderInterface for GitHubProvider {
|
|||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement GitHub-specific response parsing
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
@ -40,7 +103,7 @@ impl ProviderInterface for GitHubProvider {
|
|||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// TODO: Implement GitHub-specific request serialization
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,76 @@
|
|||
//! Groq uses OpenAI-compatible format but may have provider-specific nuances.
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Groq provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GroqProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for GroqProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for GroqProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for GroqProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for GroqProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
matches!(api_path, "/v1/chat/completions" | "/openai/v1/chat/completions")
|
||||
|
|
@ -21,21 +84,21 @@ impl ProviderInterface for GroqProvider {
|
|||
}
|
||||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,76 @@
|
|||
//! Mistral provider implementation
|
||||
|
||||
use crate::providers::{ProviderInterface, ConversionMode};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse};
|
||||
use crate::apis::openai::{ChatCompletionsRequest, ChatCompletionsResponse, Usage};
|
||||
use crate::providers::traits::{ProviderRequest, ProviderResponse, StreamingResponse};
|
||||
use crate::providers::openai::provider::{OpenAIProvider, OpenAIStreamingResponse, OpenAIApiError};
|
||||
|
||||
/// Mistral provider implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MistralProvider;
|
||||
|
||||
// Trait implementations that delegate to OpenAI
|
||||
impl ProviderRequest for MistralProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::try_from_bytes(&openai_provider, bytes)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, provider: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::to_provider_bytes(&openai_provider, request, provider, mode)
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::set_streaming_options(&openai_provider, request)
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderRequest::extract_messages_text(&openai_provider, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for MistralProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
ProviderResponse::extract_usage_counts(&openai_provider, response)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for MistralProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let openai_provider = OpenAIProvider;
|
||||
StreamingResponse::try_from_bytes(&openai_provider, bytes, provider, mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for MistralProvider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
matches!(api_path, "/v1/chat/completions")
|
||||
|
|
@ -18,21 +81,21 @@ impl ProviderInterface for MistralProvider {
|
|||
}
|
||||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_to_bytes(&self, request: &ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,18 +134,140 @@ impl Provider {
|
|||
Provider::GitHub(_, id) => *id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the provider interface implementation
|
||||
pub fn interface(&self) -> &dyn ProviderInterface {
|
||||
// Implement traits directly on the Provider enum
|
||||
impl ProviderRequest for Provider {
|
||||
type Error = openai::provider::OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<crate::apis::openai::ChatCompletionsRequest, Self::Error> {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => provider,
|
||||
Provider::Groq(provider, _) => provider,
|
||||
Provider::Mistral(provider, _) => provider,
|
||||
Provider::Deepseek(provider, _) => provider,
|
||||
Provider::Arch(provider, _) => provider,
|
||||
Provider::Gemini(provider, _) => provider,
|
||||
Provider::Claude(provider, _) => provider,
|
||||
Provider::GitHub(provider, _) => provider,
|
||||
Provider::OpenAI(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Groq(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Mistral(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Deepseek(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Arch(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Gemini(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::Claude(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
Provider::GitHub(provider, _) => ProviderRequest::try_from_bytes(provider, bytes),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, request: &crate::apis::openai::ChatCompletionsRequest, provider_id: super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Groq(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Mistral(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Deepseek(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Arch(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Gemini(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::Claude(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
Provider::GitHub(provider, _) => ProviderRequest::to_provider_bytes(provider, request, provider_id, mode),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_model<'a>(&self, request: &'a crate::apis::openai::ChatCompletionsRequest) -> &'a str {
|
||||
// Since all providers use the same implementation, just use the first one
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> bool {
|
||||
// Since all providers use the same implementation, just use the first one
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&self, request: &mut crate::apis::openai::ChatCompletionsRequest) {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Groq(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Mistral(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Deepseek(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Arch(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Gemini(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::Claude(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
Provider::GitHub(provider, _) => ProviderRequest::set_streaming_options(provider, request),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> String {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Groq(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Mistral(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Deepseek(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Arch(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Gemini(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::Claude(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
Provider::GitHub(provider, _) => ProviderRequest::extract_messages_text(provider, request),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for Provider {
|
||||
type Error = openai::provider::OpenAIApiError;
|
||||
type Usage = crate::apis::openai::Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider_id: &super::ProviderId, mode: ConversionMode) -> Result<crate::apis::openai::ChatCompletionsResponse, Self::Error> {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Groq(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Mistral(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Deepseek(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Arch(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Gemini(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Claude(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::GitHub(provider, _) => ProviderResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
}
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a crate::apis::openai::ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
// Since all providers use the same implementation, just use the direct implementation
|
||||
Some(&response.usage)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for Provider {
|
||||
type Error = openai::provider::OpenAIApiError;
|
||||
type StreamingIter = openai::provider::OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider_id: &super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Groq(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Mistral(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Deepseek(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Arch(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Gemini(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::Claude(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
Provider::GitHub(provider, _) => StreamingResponse::try_from_bytes(provider, bytes, provider_id, mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProviderInterface for Provider {
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Groq(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Mistral(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Deepseek(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Arch(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Gemini(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::Claude(provider, _) => provider.has_compatible_api(api_path),
|
||||
Provider::GitHub(provider, _) => provider.has_compatible_api(api_path),
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_apis(&self) -> Vec<&'static str> {
|
||||
match self {
|
||||
Provider::OpenAI(provider, _) => provider.supported_apis(),
|
||||
Provider::Groq(provider, _) => provider.supported_apis(),
|
||||
Provider::Mistral(provider, _) => provider.supported_apis(),
|
||||
Provider::Deepseek(provider, _) => provider.supported_apis(),
|
||||
Provider::Arch(provider, _) => provider.supported_apis(),
|
||||
Provider::Gemini(provider, _) => provider.supported_apis(),
|
||||
Provider::Claude(provider, _) => provider.supported_apis(),
|
||||
Provider::GitHub(provider, _) => provider.supported_apis(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,64 +77,58 @@ impl ProviderInterface for OpenAIProvider {
|
|||
}
|
||||
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<crate::apis::openai::ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
use crate::providers::traits::ProviderRequest;
|
||||
match ChatCompletionsRequest::try_from_bytes(bytes) {
|
||||
match ProviderRequest::try_from_bytes(self, bytes) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<crate::apis::openai::ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
use crate::providers::traits::ProviderResponse;
|
||||
match ChatCompletionsResponse::try_from_bytes(bytes, &provider_id, mode) {
|
||||
match ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_to_bytes(&self, request: &crate::apis::openai::ChatCompletionsRequest, provider_id: super::super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
use crate::providers::traits::ProviderRequest;
|
||||
match request.to_provider_bytes(provider_id, mode) {
|
||||
match ProviderRequest::to_provider_bytes(self, request, provider_id, mode) {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Trait Implementations for OpenAI Types
|
||||
// ============================================================================
|
||||
|
||||
impl ProviderRequest for ChatCompletionsRequest {
|
||||
// Direct trait implementations on OpenAIProvider
|
||||
impl ProviderRequest for OpenAIProvider {
|
||||
type Error = OpenAIApiError;
|
||||
|
||||
fn try_from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<ChatCompletionsRequest, Self::Error> {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
Ok(serde_json::from_str(s)?)
|
||||
}
|
||||
|
||||
fn to_provider_bytes(&self, _provider: super::super::ProviderId, _mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
Ok(serde_json::to_vec(self)?)
|
||||
fn to_provider_bytes(&self, request: &ChatCompletionsRequest, _provider: super::super::ProviderId, _mode: ConversionMode) -> Result<Vec<u8>, Self::Error> {
|
||||
Ok(serde_json::to_vec(request)?)
|
||||
}
|
||||
|
||||
fn extract_model(&self) -> &str {
|
||||
&self.model
|
||||
fn extract_model<'a>(&self, request: &'a ChatCompletionsRequest) -> &'a str {
|
||||
&request.model
|
||||
}
|
||||
|
||||
fn is_streaming(&self) -> bool {
|
||||
self.stream.unwrap_or_default()
|
||||
fn is_streaming(&self, request: &ChatCompletionsRequest) -> bool {
|
||||
request.stream.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_streaming_options(&mut self) {
|
||||
if self.stream_options.is_none() {
|
||||
self.stream_options = Some(StreamOptions {
|
||||
fn set_streaming_options(&self, request: &mut ChatCompletionsRequest) {
|
||||
if request.stream_options.is_none() {
|
||||
request.stream_options = Some(StreamOptions {
|
||||
include_usage: Some(true),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_messages_text(&self) -> String {
|
||||
self.messages
|
||||
fn extract_messages_text(&self, request: &ChatCompletionsRequest) -> String {
|
||||
request.messages
|
||||
.iter()
|
||||
.fold(String::new(), |acc, m| {
|
||||
acc + " " + &match &m.content {
|
||||
|
|
@ -150,8 +144,41 @@ impl ProviderRequest for ChatCompletionsRequest {
|
|||
}
|
||||
}
|
||||
|
||||
// Implement the helper trait for stream context integration
|
||||
impl crate::providers::traits::StreamContextHelpers for ChatCompletionsRequest {}
|
||||
impl ProviderResponse for OpenAIProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], _provider: &super::super::ProviderId, _mode: ConversionMode) -> Result<ChatCompletionsResponse, Self::Error> {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
Ok(serde_json::from_str(s)?)
|
||||
}
|
||||
|
||||
fn usage<'a>(&self, response: &'a ChatCompletionsResponse) -> Option<&'a Self::Usage> {
|
||||
Some(&response.usage)
|
||||
}
|
||||
|
||||
fn extract_usage_counts(&self, response: &ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
Some((
|
||||
response.usage.prompt_tokens as usize,
|
||||
response.usage.completion_tokens as usize,
|
||||
response.usage.total_tokens as usize,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamingResponse for OpenAIProvider {
|
||||
type Error = OpenAIApiError;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(&self, bytes: &[u8], _provider: &super::super::ProviderId, _mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
Ok(OpenAIStreamingResponse::new(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Trait Implementations for OpenAI Types (Keep for TokenUsage only)
|
||||
// ============================================================================
|
||||
|
||||
impl TokenUsage for Usage {
|
||||
fn completion_tokens(&self) -> usize {
|
||||
|
|
@ -167,20 +194,6 @@ impl TokenUsage for Usage {
|
|||
}
|
||||
}
|
||||
|
||||
impl ProviderResponse for ChatCompletionsResponse {
|
||||
type Error = OpenAIApiError;
|
||||
type Usage = Usage;
|
||||
|
||||
fn try_from_bytes(bytes: &[u8], _provider: &super::super::ProviderId, _mode: ConversionMode) -> Result<Self, Self::Error> {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
Ok(serde_json::from_str(s)?)
|
||||
}
|
||||
|
||||
fn usage(&self) -> Option<&Self::Usage> {
|
||||
Some(&self.usage)
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamChunk for ChatCompletionsStreamResponse {
|
||||
type Usage = Usage;
|
||||
|
||||
|
|
@ -191,9 +204,9 @@ impl StreamChunk for ChatCompletionsStreamResponse {
|
|||
|
||||
impl StreamingResponse for OpenAIStreamingResponse {
|
||||
type Error = OpenAIApiError;
|
||||
type Chunk = ChatCompletionsStreamResponse;
|
||||
type StreamingIter = OpenAIStreamingResponse;
|
||||
|
||||
fn try_from_bytes(bytes: &[u8], _provider: &super::super::ProviderId, _mode: ConversionMode) -> Result<Self, Self::Error> {
|
||||
fn try_from_bytes(&self, bytes: &[u8], _provider: &super::super::ProviderId, _mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error> {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
Ok(OpenAIStreamingResponse::new(s.to_string()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,26 +15,26 @@ pub enum ConversionMode {
|
|||
}
|
||||
|
||||
/// Trait for provider-specific request types
|
||||
pub trait ProviderRequest: Sized {
|
||||
pub trait ProviderRequest {
|
||||
type Error: Error + Send + Sync + 'static;
|
||||
|
||||
/// Parse request from raw bytes
|
||||
fn try_from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>;
|
||||
fn try_from_bytes(&self, bytes: &[u8]) -> Result<crate::apis::openai::ChatCompletionsRequest, Self::Error>;
|
||||
|
||||
/// Convert to provider-specific format
|
||||
fn to_provider_bytes(&self, provider: super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error>;
|
||||
fn to_provider_bytes(&self, request: &crate::apis::openai::ChatCompletionsRequest, provider: super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Self::Error>;
|
||||
|
||||
/// Extract the model name from the request
|
||||
fn extract_model(&self) -> &str;
|
||||
fn extract_model<'a>(&self, request: &'a crate::apis::openai::ChatCompletionsRequest) -> &'a str;
|
||||
|
||||
/// Check if this is a streaming request
|
||||
fn is_streaming(&self) -> bool;
|
||||
fn is_streaming(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> bool;
|
||||
|
||||
/// Set streaming options (e.g., include_usage)
|
||||
fn set_streaming_options(&mut self);
|
||||
fn set_streaming_options(&self, request: &mut crate::apis::openai::ChatCompletionsRequest);
|
||||
|
||||
/// Extract text content from messages for token counting
|
||||
fn extract_messages_text(&self) -> String;
|
||||
fn extract_messages_text(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> String;
|
||||
}
|
||||
|
||||
/// Trait for token usage information
|
||||
|
|
@ -45,39 +45,19 @@ pub trait TokenUsage {
|
|||
}
|
||||
|
||||
/// Trait for provider-specific response types
|
||||
pub trait ProviderResponse: Sized {
|
||||
pub trait ProviderResponse {
|
||||
type Error: Error + Send + Sync + 'static;
|
||||
type Usage: TokenUsage;
|
||||
|
||||
/// Parse response from raw bytes
|
||||
fn try_from_bytes(bytes: &[u8], provider: &super::ProviderId, mode: ConversionMode) -> Result<Self, Self::Error>;
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::ProviderId, mode: ConversionMode) -> Result<crate::apis::openai::ChatCompletionsResponse, Self::Error>;
|
||||
|
||||
/// Get usage information if available
|
||||
fn usage(&self) -> Option<&Self::Usage>;
|
||||
fn usage<'a>(&self, response: &'a crate::apis::openai::ChatCompletionsResponse) -> Option<&'a Self::Usage>;
|
||||
|
||||
/// Extract token counts for metrics
|
||||
fn extract_usage_counts(&self) -> Option<(usize, usize, usize)> {
|
||||
self.usage().map(|u| (u.prompt_tokens(), u.completion_tokens(), u.total_tokens()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait for stream context integration
|
||||
pub trait StreamContextHelpers: ProviderRequest {
|
||||
/// Get the model name for routing and metrics
|
||||
fn get_model_for_routing(&self) -> String {
|
||||
self.extract_model().to_string()
|
||||
}
|
||||
|
||||
/// Get text for token counting and rate limiting
|
||||
fn get_text_for_tokenization(&self) -> String {
|
||||
self.extract_messages_text()
|
||||
}
|
||||
|
||||
/// Prepare for streaming by setting appropriate options
|
||||
fn prepare_for_streaming(&mut self) {
|
||||
if self.is_streaming() {
|
||||
self.set_streaming_options();
|
||||
}
|
||||
fn extract_usage_counts(&self, response: &crate::apis::openai::ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
self.usage(response).map(|u| (u.prompt_tokens(), u.completion_tokens(), u.total_tokens()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,70 +70,34 @@ pub trait StreamChunk {
|
|||
}
|
||||
|
||||
/// Trait for streaming response iterators
|
||||
pub trait StreamingResponse: Iterator<Item = Result<Self::Chunk, Self::Error>> + Sized {
|
||||
pub trait StreamingResponse {
|
||||
type Error: Error + Send + Sync + 'static;
|
||||
type Chunk: StreamChunk;
|
||||
type StreamingIter: Iterator<Item = Result<crate::apis::openai::ChatCompletionsStreamResponse, Self::Error>>;
|
||||
|
||||
/// Parse streaming response from raw bytes
|
||||
fn try_from_bytes(bytes: &[u8], provider: &super::ProviderId, mode: ConversionMode) -> Result<Self, Self::Error>;
|
||||
fn try_from_bytes(&self, bytes: &[u8], provider: &super::ProviderId, mode: ConversionMode) -> Result<Self::StreamingIter, Self::Error>;
|
||||
}
|
||||
|
||||
/// Main provider interface trait
|
||||
pub trait ProviderInterface {
|
||||
/// Main provider interface trait - simplified to only essential methods
|
||||
pub trait ProviderInterface: ProviderRequest + ProviderResponse + StreamingResponse {
|
||||
/// Check if this provider has a compatible API with the client request
|
||||
fn has_compatible_api(&self, api_path: &str) -> bool;
|
||||
|
||||
/// Get the interface implementation for this provider
|
||||
/// passthrough: if true, use provider-specific format; if false, use compatible format
|
||||
fn get_interface(&self, passthrough: bool) -> ConversionMode {
|
||||
if passthrough {
|
||||
ConversionMode::Passthrough
|
||||
} else {
|
||||
ConversionMode::Compatible
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a request from raw bytes - returns concrete ChatCompletionsRequest
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<crate::apis::openai::ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
/// Parse a response from raw bytes - returns concrete ChatCompletionsResponse
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::ProviderId, mode: ConversionMode) -> Result<crate::apis::openai::ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
/// Convert a request to bytes for sending to upstream API
|
||||
fn request_to_bytes(&self, request: &crate::apis::openai::ChatCompletionsRequest, provider_id: super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
/// Extract model name from request for routing (convenience method for stream_context)
|
||||
fn extract_model_from_request(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> String {
|
||||
use ProviderRequest;
|
||||
request.extract_model().to_string()
|
||||
}
|
||||
|
||||
/// Check if request is streaming (convenience method for stream_context)
|
||||
fn is_request_streaming(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> bool {
|
||||
use ProviderRequest;
|
||||
request.is_streaming()
|
||||
}
|
||||
|
||||
/// Prepare request for streaming (convenience method for stream_context)
|
||||
fn prepare_request_for_streaming(&self, request: &mut crate::apis::openai::ChatCompletionsRequest) {
|
||||
use ProviderRequest;
|
||||
if request.is_streaming() {
|
||||
request.set_streaming_options();
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract text for tokenization (convenience method for stream_context)
|
||||
fn extract_text_for_tokenization(&self, request: &crate::apis::openai::ChatCompletionsRequest) -> String {
|
||||
use ProviderRequest;
|
||||
request.extract_messages_text()
|
||||
}
|
||||
|
||||
/// Extract usage information from response (convenience method for stream_context)
|
||||
fn extract_usage_from_response(&self, response: &crate::apis::openai::ChatCompletionsResponse) -> Option<(usize, usize, usize)> {
|
||||
use ProviderResponse;
|
||||
response.extract_usage_counts()
|
||||
}
|
||||
|
||||
/// Get supported API endpoints for this provider
|
||||
fn supported_apis(&self) -> Vec<&'static str>;
|
||||
|
||||
/// Parse a request from raw bytes - delegates to ProviderRequest
|
||||
fn parse_request(&self, bytes: &[u8]) -> Result<crate::apis::openai::ChatCompletionsRequest, Box<dyn std::error::Error + Send + Sync>> {
|
||||
ProviderRequest::try_from_bytes(self, bytes).map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
|
||||
/// Parse a response from raw bytes - delegates to ProviderResponse
|
||||
fn parse_response(&self, bytes: &[u8], provider_id: super::ProviderId, mode: ConversionMode) -> Result<crate::apis::openai::ChatCompletionsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||
ProviderResponse::try_from_bytes(self, bytes, &provider_id, mode).map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
|
||||
/// Convert a request to bytes - delegates to ProviderRequest
|
||||
fn request_to_bytes(&self, request: &crate::apis::openai::ChatCompletionsRequest, provider_id: super::ProviderId, mode: ConversionMode) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
ProviderRequest::to_provider_bytes(self, request, provider_id, mode).map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue