From 2e3a61d7561cdcf679858120b79b280fb75bf4c6 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sun, 17 Aug 2025 14:43:35 -0300 Subject: [PATCH] - Open AI compatible endpoint in GB. --- api.json | 537 -------------------------------- src/main.rs | 23 +- src/scripts/utils/set-limits.sh | 8 +- src/services/llm_local.rs | 14 +- src/services/llm_provider.rs | 33 +- 5 files changed, 46 insertions(+), 569 deletions(-) delete mode 100644 api.json diff --git a/api.json b/api.json deleted file mode 100644 index 532547c..0000000 --- a/api.json +++ /dev/null @@ -1,537 +0,0 @@ -openapi: 3.0.0 -info: - title: General Bots API - description: API for managing files, documents, groups, conversations, and more. - version: 1.0.0 -servers: - - url: https://api.generalbots.com/v1 - description: Production server -paths: - /files/upload: - post: - summary: Upload a file - operationId: uploadFile - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - file: - type: string - format: binary - responses: - '200': - description: File uploaded successfully - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - url: - type: string - - /files/download: - post: - summary: Download a file - operationId: downloadFile - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - responses: - '200': - description: File downloaded successfully - content: - application/octet-stream: - schema: - type: string - format: binary - - /files/copy: - post: - summary: Copy a file - operationId: copyFile - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - sourcePath: - type: string - destinationPath: - type: string - responses: - '200': - description: File copied successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/move: - post: - summary: Move a file - operationId: moveFile - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - sourcePath: - type: string - destinationPath: - type: string - responses: - '200': - description: File moved successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/delete: - post: - summary: Delete a file - operationId: deleteFile - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - responses: - '200': - description: File deleted successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/getContents: - post: - summary: Get file contents - operationId: getFileContents - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - responses: - '200': - description: File contents retrieved successfully - content: - application/json: - schema: - type: object - properties: - contents: - type: string - - /files/save: - post: - summary: Save a file - operationId: saveFile - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - contents: - type: string - responses: - '200': - description: File saved successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/createFolder: - post: - summary: Create a folder - operationId: createFolder - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - folderName: - type: string - parentFolderId: - type: string - responses: - '200': - description: Folder created successfully - content: - application/json: - schema: - type: object - properties: - folderId: - type: string - - /files/shareFolder: - post: - summary: Share a folder - operationId: shareFolder - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - folderId: - type: string - userIds: - type: array - items: - type: string - responses: - '200': - description: Folder shared successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/dirFolder: - post: - summary: List folder contents - operationId: dirFolder - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - folderId: - type: string - responses: - '200': - description: Folder contents retrieved successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - size: - type: integer - - /files/list: - post: - summary: List files - operationId: getFiles - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - folderId: - type: string - responses: - '200': - description: Files listed successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - size: - type: integer - - /files/search: - post: - summary: Search files - operationId: searchFiles - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - query: - type: string - responses: - '200': - description: Files searched successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - size: - type: integer - - /files/recent: - post: - summary: Get recent files - operationId: getRecentFiles - responses: - '200': - description: Recent files retrieved successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - size: - type: integer - - /files/favorite: - post: - summary: Toggle favorite status of a file - operationId: toggleFavorite - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - responses: - '200': - description: Favorite status toggled successfully - content: - application/json: - schema: - type: object - properties: - isFavorite: - type: boolean - - /files/versions: - post: - summary: Get file versions - operationId: getFileVersions - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - responses: - '200': - description: File versions retrieved successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - versionId: - type: string - timestamp: - type: string - size: - type: integer - - /files/restore: - post: - summary: Restore a file version - operationId: restoreFileVersion - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - versionId: - type: string - responses: - '200': - description: File version restored successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/permissions: - post: - summary: Set file permissions - operationId: setFilePermissions - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - fileId: - type: string - permissions: - type: object - responses: - '200': - description: File permissions updated successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/quota: - get: - summary: Get storage quota - operationId: getStorageQuota - responses: - '200': - description: Storage quota retrieved successfully - content: - application/json: - schema: - type: object - properties: - used: - type: integer - total: - type: integer - - /files/shared: - get: - summary: Get shared files - operationId: getSharedFiles - responses: - '200': - description: Shared files retrieved successfully - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - type: - type: string - size: - type: integer - - /files/sync/status: - get: - summary: Get sync status - operationId: getSyncStatus - responses: - '200': - description: Sync status retrieved successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - - /files/sync/start: - post: - summary: Start sync - operationId: startSync - responses: - '200': - description: Sync started successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string - - /files/sync/stop: - post: - summary: Stop sync - operationId: stopSync - responses: - '200': - description: Sync stopped successfully - content: - application/json: - schema: - type: object - properties: - message: - type: string \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e6264a4..424afa6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ +use actix_web::middleware::Logger; use std::sync::Arc; -use actix_cors::Cors; -use actix_web::http::header; use actix_web::{web, App, HttpServer}; use dotenv::dotenv; use services::state::*; @@ -11,6 +10,7 @@ use sqlx::PgPool; use crate::services::automation::AutomationService; use crate::services::email::{get_emails, list_emails, save_click, send_email}; use crate::services::llm::{chat, chat_stream}; +use crate::services::llm_local::chat_completions_local; use crate::services::llm_provider::chat_completions; use crate::services::web_automation::{initialize_browser_pool, BrowserPool}; @@ -38,7 +38,7 @@ async fn main() -> std::io::Result<()> { "/usr/bin/brave-browser-beta".to_string(), )); - #[cfg(feature = "local_llm")] + #[cfg(feature = "default")] { use crate::services::llm_local::ensure_llama_server_running; @@ -67,15 +67,17 @@ async fn main() -> std::io::Result<()> { // Start HTTP server HttpServer::new(move || { - let cors = Cors::default() - .send_wildcard() - .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) - .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) - .allowed_header(header::CONTENT_TYPE) - .max_age(3600); + // let cors = Cors::default() + // .send_wildcard() + // .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) + // .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + // .allowed_header(header::CONTENT_TYPE) + // .max_age(3600); + //.wrap(cors) App::new() - .wrap(cors) + .wrap(Logger::default()) + .wrap(Logger::new("%a %{User-Agent}i")) .app_data(app_state.clone()) .service(upload_file) .service(list_file) @@ -85,6 +87,7 @@ async fn main() -> std::io::Result<()> { .service(send_email) .service(chat_stream) .service(chat_completions) + .service(chat_completions_local) .service(chat) }) .bind((config.server.host.clone(), config.server.port))? diff --git a/src/scripts/utils/set-limits.sh b/src/scripts/utils/set-limits.sh index 0c81ed0..f5463bc 100644 --- a/src/scripts/utils/set-limits.sh +++ b/src/scripts/utils/set-limits.sh @@ -27,16 +27,16 @@ CPU_PRIORITY=5 for pattern in "${!container_limits[@]}"; do echo "Configuring $container..." - + memory=$DEFAULT_MEMORY cpu_allowance=$DEFAULT_CPU_ALLOWANCE - + # Configure all containers for container in $(lxc list -c n --format csv); do # Check if container matches any pattern if [[ $container == $pattern ]]; then IFS=':' read -r memory cpu_allowance <<< "${container_limits[$pattern]}" - + # Apply configuration lxc config set "$container" limits.memory "$memory" lxc config set "$container" limits.cpu.allowance "$cpu_allowance" @@ -50,4 +50,4 @@ for pattern in "${!container_limits[@]}"; do break fi done -done \ No newline at end of file +done diff --git a/src/services/llm_local.rs b/src/services/llm_local.rs index 926548c..bef0973 100644 --- a/src/services/llm_local.rs +++ b/src/services/llm_local.rs @@ -93,7 +93,7 @@ async fn start_llama_server() -> Result<(), Box Result<(), Box String { } // Proxy endpoint -#[post("/v1/chat/completions")] -pub async fn chat_completions( +#[post("/v1/chat/completions1")] +pub async fn chat_completions_local( req_body: web::Json, _req: HttpRequest, ) -> Result { - dotenv().ok(); + dotenv().ok().unwrap(); // Ensure llama.cpp server is running if let Err(e) = ensure_llama_server_running().await { diff --git a/src/services/llm_provider.rs b/src/services/llm_provider.rs index 9af6251..05ac6bd 100644 --- a/src/services/llm_provider.rs +++ b/src/services/llm_provider.rs @@ -1,5 +1,6 @@ use actix_web::{post, web, HttpRequest, HttpResponse, Result}; use dotenv::dotenv; +use regex::Regex; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::env; @@ -33,12 +34,15 @@ struct Choice { finish_reason: String, } -// Proxy endpoint #[post("/v1/chat/completions")] -async fn chat_completions( - req_body: web::Json, - _req: HttpRequest, -) -> Result { +async fn chat_completions(body: web::Bytes, _req: HttpRequest) -> Result { + // Always log raw POST data + if let Ok(body_str) = std::str::from_utf8(&body) { + println!("POST Data: {}", body_str); + } else { + println!("POST Data (binary): {:?}", body); + } + dotenv().ok(); // Environment variables @@ -67,12 +71,23 @@ async fn chat_completions( reqwest::header::HeaderValue::from_static("application/json"), ); + let body_str = std::str::from_utf8(&body).unwrap_or(""); + println!("Original POST Data: {}", body_str); + + // Remove the problematic params + let re = + Regex::new(r#","?\s*"(max_completion_tokens|parallel_tool_calls)"\s*:\s*[^,}]*"#).unwrap(); + let cleaned = re.replace_all(body_str, ""); + let cleaned_body = web::Bytes::from(cleaned.to_string()); + + println!("Cleaned POST Data: {}", cleaned); + // Send request to Azure let client = Client::new(); let response = client .post(&url) .headers(headers) - .json(&req_body.into_inner()) + .body(cleaned_body) .send() .await .map_err(actix_web::error::ErrorInternalServerError)?; @@ -88,11 +103,7 @@ async fn chat_completions( println!("Raw Azure response: {}", raw_response); if status.is_success() { - // Parse the raw response as JSON - let azure_response: serde_json::Value = serde_json::from_str(&raw_response) - .map_err(actix_web::error::ErrorInternalServerError)?; - - Ok(HttpResponse::Ok().json(azure_response)) + Ok(HttpResponse::Ok().body(raw_response)) } else { // Handle error responses properly let actix_status = actix_web::http::StatusCode::from_u16(status.as_u16())