diff --git a/README.md b/README.md index 25685f18..2ae57e6d 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,18 @@ Arch Gateway Logo

-## Build fast, robust, and personalized GenAI apps (agents, assistants, etc.) +## Build fast, robust, and personalized AI agents. -Arch is an intelligent [Layer 7](https://www.cloudflare.com/learning/ddos/what-is-layer-7/) gateway designed for generative AI apps, AI agents, and co-pilots that work with prompts. Engineered with purpose-built LLMs, Arch handles the critical but undifferentiated tasks related to the handling and processing of prompts, including detecting and rejecting [jailbreak](https://github.com/verazuo/jailbreak_llms) attempts, intelligently calling "backend" APIs to fulfill the user's request represented in a prompt, routing to and offering disaster recovery between upstream LLMs, and managing the observability of prompts and LLM interactions in a centralized way. +Arch is an intelligent [Layer 7](https://www.cloudflare.com/learning/ddos/what-is-layer-7/) gateway designed to protect, observe, and personalize LLM applications (agents, assistants, co-pilots) with your APIs. + +Engineered with purpose-built LLMs, Arch handles the critical but undifferentiated tasks related to the handling and processing of prompts, including detecting and rejecting [jailbreak](https://github.com/verazuo/jailbreak_llms) attempts, intelligently calling "backend" APIs to fulfill the user's request represented in a prompt, routing to and offering disaster recovery between upstream LLMs, and managing the observability of prompts and LLM interactions in a centralized way. Arch is built on (and by the core contributors of) [Envoy Proxy](https://www.envoyproxy.io/) with the belief that: >Prompts are nuanced and opaque user requests, which require the same capabilities as traditional HTTP requests including secure handling, intelligent routing, robust observability, and integration with backend (API) systems for personalization – all outside business logic.* **Core Features**: - - Built on [Envoy](https://envoyproxy.io): Arch runs alongside application servers, and builds on top of Envoy's proven HTTP management and scalability features to handle ingress and egress traffic related to prompts and LLMs + - Built on [Envoy](https://envoyproxy.io): Arch runs alongside application servers, and builds on top of Envoy's proven HTTP management and scalability features to handle ingress and egress traffic related to prompts and LLMs. - Function Calling for fast Agentic and RAG apps. Engineered with purpose-built [LLMs](https://huggingface.co/collections/katanemo/arch-function-66f209a693ea8df14317ad68) to handle fast, cost-effective, and accurate prompt-based tasks like function/API calling, and parameter extraction from prompts. - Prompt [Guard](https://huggingface.co/collections/katanemo/arch-guard-6702bdc08b889e4bce8f446d): Arch centralizes prompt guardrails to prevent jailbreak attempts and ensure safe user interactions without writing a single line of code. - Traffic Management: Arch manages LLM calls, offering smart retries, automatic cutover, and resilient upstream connections for continuous availability. @@ -20,7 +22,7 @@ Arch is an intelligent [Layer 7](https://www.cloudflare.com/learning/ddos/what-i **Jump to our [docs](https://docs.archgw.com)** to learn how you can use Arch to improve the speed, security and personalization of your GenAI apps. ## Contact -To get in touch with us, please join our [discord server](https://discord.gg/rbjqVbpa). We will be monitoring that actively and offering support there. +To get in touch with us, please join our [discord server](https://discord.gg/rSRQ9fv7). We will be monitoring that actively and offering support there. ## Demos * [Function Calling](demos/function_calling/README.md) - Walk through of critical function calling capabilities @@ -35,7 +37,7 @@ Follow this guide to learn how to quickly set up Arch and integrate it into your Before you begin, ensure you have the following: -- `Docker` & `Python` verion 3.10 installed on your system +- `Docker` & `Python` installed on your system - `API Keys` for LLM providers (if using external LLMs) ### Step 1: Install Arch @@ -69,7 +71,7 @@ llm_providers: - name: OpenAI provider: openai access_key: OPENAI_API_KEY - model: gpt-3.5-turbo + model: gpt-4o default: true stream: true @@ -109,15 +111,12 @@ Make outbound calls via Arch import openai # Set the OpenAI API base URL to the Arch gateway endpoint -openai.api_base = "http://127.0.0.1:12000/" +openai.api_base = "http://127.0.0.1:51001/v1" # No need to set openai.api_key since it's configured in Arch's gateway # Use the OpenAI client as usual -# we set api_key to '--' becasue openai client would fail to initiate request without it. Just pass any -# dummy value here since arch gateway will properly pass access key before making outbound call. response = openai.Completion.create( - api_key="--", model="text-davinci-003", prompt="What is the capital of France?" ) diff --git a/arch/src/consts.rs b/arch/src/consts.rs index 32172002..76244f6b 100644 --- a/arch/src/consts.rs +++ b/arch/src/consts.rs @@ -1,7 +1,7 @@ pub const DEFAULT_EMBEDDING_MODEL: &str = "katanemo/bge-large-en-v1.5"; pub const DEFAULT_INTENT_MODEL: &str = "katanemo/bart-large-mnli"; pub const DEFAULT_PROMPT_TARGET_THRESHOLD: f64 = 0.8; -pub const DEFAULT_HALLUCINATED_THRESHOLD: f64 = 0.1; +pub const DEFAULT_HALLUCINATED_THRESHOLD: f64 = 0.25; pub const RATELIMIT_SELECTOR_HEADER_KEY: &str = "x-arch-ratelimit-selector"; pub const SYSTEM_ROLE: &str = "system"; pub const USER_ROLE: &str = "user"; @@ -19,3 +19,4 @@ pub const REQUEST_ID_HEADER: &str = "x-request-id"; pub const ARCH_INTERNAL_CLUSTER_NAME: &str = "arch_internal"; pub const ARCH_UPSTREAM_HOST_HEADER: &str = "x-arch-upstream"; pub const ARCH_LLM_UPSTREAM_LISTENER: &str = "arch_llm_listener"; +pub const ARCH_MODEL_PREFIX: &str = "Arch"; diff --git a/arch/src/stream_context.rs b/arch/src/stream_context.rs index bc9e62fa..7a65609c 100644 --- a/arch/src/stream_context.rs +++ b/arch/src/stream_context.rs @@ -1,9 +1,9 @@ use crate::consts::{ ARCH_FC_MODEL_NAME, ARCH_FC_REQUEST_TIMEOUT_MS, ARCH_INTERNAL_CLUSTER_NAME, - ARCH_LLM_UPSTREAM_LISTENER, ARCH_MESSAGES_KEY, ARCH_PROVIDER_HINT_HEADER, ARCH_ROUTING_HEADER, - ARCH_STATE_HEADER, ARCH_UPSTREAM_HOST_HEADER, ARC_FC_CLUSTER, CHAT_COMPLETIONS_PATH, - DEFAULT_EMBEDDING_MODEL, DEFAULT_HALLUCINATED_THRESHOLD, DEFAULT_INTENT_MODEL, - DEFAULT_PROMPT_TARGET_THRESHOLD, GPT_35_TURBO, MODEL_SERVER_NAME, + ARCH_LLM_UPSTREAM_LISTENER, ARCH_MESSAGES_KEY, ARCH_MODEL_PREFIX, ARCH_PROVIDER_HINT_HEADER, + ARCH_ROUTING_HEADER, ARCH_STATE_HEADER, ARCH_UPSTREAM_HOST_HEADER, ARC_FC_CLUSTER, + CHAT_COMPLETIONS_PATH, DEFAULT_EMBEDDING_MODEL, DEFAULT_HALLUCINATED_THRESHOLD, + DEFAULT_INTENT_MODEL, DEFAULT_PROMPT_TARGET_THRESHOLD, GPT_35_TURBO, MODEL_SERVER_NAME, RATELIMIT_SELECTOR_HEADER_KEY, REQUEST_ID_HEADER, SYSTEM_ROLE, USER_ROLE, }; use crate::filter_context::{EmbeddingsStore, WasmMetrics}; @@ -453,7 +453,7 @@ impl StreamContext { if messages.len() >= 2 { let latest_assistant_message = &messages[messages.len() - 2]; if let Some(model) = latest_assistant_message.model.as_ref() { - if model.contains("Arch") { + if model.contains(ARCH_MODEL_PREFIX) { arch_assistant = true; } } @@ -728,8 +728,41 @@ impl StreamContext { None => HashMap::new(), // Return an empty HashMap if v is not an object }; + let messages = &callout_context.request_body.messages; + let mut arch_assistant = false; + let mut user_messages = Vec::new(); + + if messages.len() >= 2 { + let latest_assistant_message = &messages[messages.len() - 2]; + if let Some(model) = latest_assistant_message.model.as_ref() { + if model.starts_with(ARCH_MODEL_PREFIX) { + arch_assistant = true; + } + } + } + if arch_assistant { + for message in messages.iter() { + if let Some(model) = message.model.as_ref() { + if !model.starts_with(ARCH_MODEL_PREFIX) { + break; + } + } + if message.role == "user" { + if let Some(content) = &message.content { + user_messages.push(content.clone()); + } + } + } + } else { + if let Some(user_message) = callout_context.user_message.as_ref() { + user_messages.push(user_message.clone()); + } + } + let user_messages_str = user_messages.join(", "); + debug!("user messages: {}", user_messages_str); + let hallucination_classification_request = HallucinationClassificationRequest { - prompt: callout_context.user_message.as_ref().unwrap().clone(), + prompt: user_messages_str, model: String::from(DEFAULT_INTENT_MODEL), parameters: tool_params_dict, }; diff --git a/arch/tests/integration.rs b/arch/tests/integration.rs index 06918bdf..c628d9c3 100644 --- a/arch/tests/integration.rs +++ b/arch/tests/integration.rs @@ -586,6 +586,7 @@ fn request_ratelimited() { .expect_log(Some(LogLevel::Debug), None) .expect_log(Some(LogLevel::Debug), None) .expect_log(Some(LogLevel::Debug), None) + .expect_log(Some(LogLevel::Debug), None) .expect_http_call( Some("arch_internal"), Some(vec![ @@ -728,6 +729,7 @@ fn request_not_ratelimited() { .expect_log(Some(LogLevel::Debug), None) .expect_log(Some(LogLevel::Debug), None) .expect_log(Some(LogLevel::Debug), None) + .expect_log(Some(LogLevel::Debug), None) .expect_http_call( Some("arch_internal"), Some(vec![ diff --git a/demos/hr_agent/arch_config.yaml b/demos/hr_agent/arch_config.yaml index 4294278a..a7d75d72 100644 --- a/demos/hr_agent/arch_config.yaml +++ b/demos/hr_agent/arch_config.yaml @@ -30,7 +30,7 @@ prompt_targets: - name: region type: str required: true - description: the geographical region for which you want head count data. + description: the geographical region for which you want headcount data. - name: hr_qa endpoint: name: app_server diff --git a/docs/source/_static/img/arch-logo.png b/docs/source/_static/img/arch-logo.png index 183f6ee9..f1d11f62 100644 Binary files a/docs/source/_static/img/arch-logo.png and b/docs/source/_static/img/arch-logo.png differ diff --git a/docs/source/index.rst b/docs/source/index.rst index bda66032..dea9d8d0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -2,7 +2,7 @@ Welcome to Arch! ================ .. image:: /_static/img/arch-logo.png - :width: 80% + :width: 100% :align: center .. raw:: html diff --git a/model_server/app/main.py b/model_server/app/main.py index 6a90c0b1..c6f5752a 100644 --- a/model_server/app/main.py +++ b/model_server/app/main.py @@ -177,7 +177,7 @@ async def hallucination(req: HallucinationRequest, res: Response): """ Take input as text and return the prediction of hallucination for each parameter """ - + logger.info(f"hallucination request: {req}") if req.model != zero_shot_model["model_name"]: raise HTTPException(status_code=400, detail="unknown model: " + req.model) diff --git a/www/index.html b/www/index.html index 91735839..bd646a51 100644 --- a/www/index.html +++ b/www/index.html @@ -174,7 +174,7 @@
GitHub Docs - Discord + Discord Contact