refactor demos (#398)

This commit is contained in:
Salman Paracha 2025-02-07 18:45:42 -08:00 committed by GitHub
parent 2bd61d628c
commit b3c95a6698
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
93 changed files with 338 additions and 1042 deletions

View file

@ -7,7 +7,7 @@ on:
pull_request:
jobs:
test:
e2e_archgw_tests:
runs-on: ubuntu-latest-m
defaults:
run:

View file

@ -7,7 +7,7 @@ on:
pull_request:
jobs:
test:
e2e_model_server_tests:
runs-on: ubuntu-latest-m
defaults:
run:

View file

@ -7,7 +7,7 @@ on:
pull_request:
jobs:
test:
e2e_demo_tests:
runs-on: ubuntu-latest-m
steps:
@ -37,7 +37,7 @@ jobs:
source venv/bin/activate
cd model_server/ && echo "installing model server" && poetry install
cd ../arch/tools && echo "installing archgw cli" && poetry install
cd ../../demos/test_runner && echo "installing test dependencies" && poetry install
cd ../../demos/shared/test_runner && echo "installing test dependencies" && poetry install
- name: run demo tests
env:
@ -45,4 +45,4 @@ jobs:
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
run: |
source venv/bin/activate
cd demos/test_runner && sh run_demo_tests.sh
cd demos/shared/test_runner && sh run_demo_tests.sh

View file

@ -7,7 +7,7 @@ on:
pull_request:
jobs:
test:
e2e_tests:
runs-on: ubuntu-latest
steps:

View file

@ -38,9 +38,8 @@ Arch is engineered with purpose-built LLMs to handle critical but undifferentiat
To get in touch with us, please join our [discord server](https://discord.gg/pGZf2gcwEc). We will be monitoring that actively and offering support there.
## Demos
* [Weather Forecast](demos/weather_forecast/README.md) - Walk through of the core function calling capabilities of arch gateway using weather forecasting service
* [Insurance Agent](demos/insurance_agent/README.md) - Build a full insurance agent with Arch
* [Network Agent](demos/network_agent/README.md) - Build a networking co-pilot/agent agent with Arch
* [Weather Forecast](demos/samples_python/weather_forecast/README.md) - Walk through of the core function calling capabilities of arch gateway using weather forecasting service
* [Network Agent](demos/samples_python/network_switch_operator_agent/README.md) - Build a networking co-pilot/agent agent with Arch
## Quickstart

View file

@ -8,7 +8,7 @@ services:
- "12000:12000"
- "19901:9901"
volumes:
- ${ARCH_CONFIG_FILE:-../demos/weather_forecast/arch_config.yaml}:/app/arch_config.yaml
- ${ARCH_CONFIG_FILE:-../demos/samples_python/weather_forecast/arch_config.yaml}:/app/arch_config.yaml
- /etc/ssl/cert.pem:/etc/ssl/cert.pem
- ./envoy.template.yaml:/app/envoy.template.yaml
- ./arch_config_schema.yaml:/app/arch_config_schema.yaml

View file

@ -1,19 +0,0 @@
FROM python:3 AS base
FROM base AS builder
WORKDIR /src
COPY requirements.txt /src/
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
COPY . /src
FROM python:3-slim AS output
COPY --from=builder /runtime /usr/local
COPY . /app
WORKDIR /app
CMD ["uvicorn", "insurance_agent_main:app", "--host", "0.0.0.0", "--port", "80", "--log-level", "info"]

View file

@ -1,58 +0,0 @@
# Insurance Agent Demo
This demo showcases how the **Arch** can be used to manage insurance-related tasks such as policy inquiries, initiating policies, and updating claims or deductibles. In this demo, the assistant provides factual information related to insurance policies (e.g., car, boat, house, motorcycle).
The system can perform a variety of tasks, such as answering insurance-related questions, retrieving policy coverage details, initiating policies, and updating claims or deductibles.
## Available Functions:
- **Policy Q/A**: Handles general Q&A related to insurance policies.
- **Endpoint**: `/policy/qa`
- This function answers general inquiries related to insurance, such as coverage details or policy types. It is the default target for insurance-related queries.
- **Get Policy Coverage**: Retrieves the coverage details for a given policy type (car, boat, house, motorcycle).
- **Endpoint**: `/policy/coverage`
- Parameters:
- `policy_type` (required): The type of policy. Available options: `car`, `boat`, `house`, `motorcycle`. Defaults to `car`.
- **Initiate Policy**: Starts a policy coverage for car, boat, motorcycle, or house.
- **Endpoint**: `/policy/initiate`
- Parameters:
- `policy_type` (required): The type of policy. Available options: `car`, `boat`, `house`, `motorcycle`. Defaults to `car`.
- `deductible` (required): The deductible amount set for the policy.
- **Update Claim**: Updates the notes on a specific insurance claim.
- **Endpoint**: `/policy/claim`
- Parameters:
- `claim_id` (required): The claim number.
- `notes` (optional): Notes about the claim number for the adjustor to see.
- **Update Deductible**: Updates the deductible amount for a specific policy coverage.
- **Endpoint**: `/policy/deductible`
- Parameters:
- `policy_id` (required): The ID of the policy.
- `deductible` (required): The deductible amount to be set for the policy.
**Arch** is designed to intelligently routes prompts to the appropriate functions based on the target, allowing for seamless interaction with various insurance-related services.
# Starting the demo
1. Please make sure the [pre-requisites](https://github.com/katanemo/arch/?tab=readme-ov-file#prerequisites) are installed correctly
2. Start Arch
```sh
sh run_demo.sh
```
3. Navigate to http://localhost:18080/
4. Tell me what can you do for me?"
# Observability
Arch gateway publishes stats endpoint at http://localhost:19901/stats. In this demo we are using prometheus to pull stats from arch and we are using grafana to visalize the stats in dashboard. To see grafana dashboard follow instructions below,
1. Start grafana and prometheus using following command
```yaml
docker compose --profile monitoring up
```
1. Navigate to http://localhost:3000/ to open grafana UI (use admin/grafana as credentials)
1. From grafana left nav click on dashboards and select "Intelligent Gateway Overview" to view arch gateway stats
Here is sample interaction,
<img width="575" alt="image" src="https://github.com/user-attachments/assets/25d40f46-616e-41ea-be8e-1623055c84ec">

View file

@ -1,105 +0,0 @@
version: v0.1
listener:
address: 127.0.0.1
port: 8080 #If you configure port 443, you'll need to update the listener with tls_certificates
message_format: huggingface
system_prompt: |
You are an insurance assistant that just offers guidance related to car, boat, rental and home insurnace only. Please be pricese and summarize based on the context provided.
llm_providers:
- name: OpenAI
provider_interface: openai
access_key: $OPENAI_API_KEY
model: gpt-4o
default: true
# Arch creates a round-robin load balancing between different endpoints, managed via the cluster subsystem.
endpoints:
app_server:
# value could be ip address or a hostname with port
# this could also be a list of endpoints for load balancing
# for example endpoint: [ ip1:port, ip2:port ]
endpoint: host.docker.internal:18083
# max time to wait for a connection to be established
connect_timeout: 0.05s
prompt_targets:
- name: policy_qa
endpoint:
name: app_server
path: /policy/qa
http_method: POST
description: Handle general Q/A related to insurance.
default: true
- name: get_policy_coverage
description: Retrieve the coverage details for an insurance policy.
endpoint:
name: app_server
path: /policy/coverage
http_method: POST
parameters:
- name: policy_type
type: str
description: The type of policy
default: car
required: true
- name: initiate_policy
endpoint:
name: app_server
path: /policy/initiate
http_method: POST
description: Start a policy coverage for an insurance policy
parameters:
- name: policy_type
type: str
description: The type of policy
default: car
required: true
- name: deductible
type: float
description: the deductible amount set of the policy
required: true
- name: update_claim
endpoint:
name: app_server
path: /policy/claim
http_method: POST
description: Update the notes on the claim
parameters:
- name: claim_id
type: str
description: the claim number
required: true
- name: notes
type: str
description: notes about the cliam number for your adjustor to see
required: false
- name: update_deductible
endpoint:
name: app_server
path: /policy/deductible
http_method: POST
description: Update the deductible amount for a specific insurance policy coverage.
parameters:
- name: policy_id
type: str
description: The id of the insurance policy
required: true
- name: deductible
type: float
description: the deductible amount set of the policy
required: true
ratelimits:
- model: gpt-4
selector:
key: selector-key
value: selector-value
limit:
tokens: 1
unit: minute

View file

@ -1,140 +0,0 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
class Conversation(BaseModel):
arch_messages: list
class PolicyCoverageRequest(BaseModel):
policy_type: str = Field(
...,
description="The type of a policy held by the customer For, e.g. car, boat, house, motorcycle)",
)
class PolicyInitiateRequest(PolicyCoverageRequest):
deductible: float = Field(
..., description="The deductible amount set of the policy"
)
class ClaimUpdate(BaseModel):
claim_id: str
notes: str # Status or details of the claim
class DeductibleUpdate(BaseModel):
policy_id: str
deductible: float
class CoverageResponse(BaseModel):
policy_type: str
coverage: str # Description of coverage
premium: float # The premium cost
# Get information about policy coverage
@app.post("/policy/coverage", response_model=CoverageResponse)
async def get_policy_coverage(req: PolicyCoverageRequest):
"""
Retrieve the coverage details for a given policy type (car, boat, house, motorcycle).
"""
policy_coverage = {
"car": {
"coverage": "Full car coverage with collision, liability",
"premium": 500.0,
},
"boat": {
"coverage": "Full boat coverage including theft and storm damage",
"premium": 700.0,
},
"house": {
"coverage": "Full house coverage including fire, theft, flood",
"premium": 1000.0,
},
"motorcycle": {
"coverage": "Full motorcycle coverage with liability",
"premium": 400.0,
},
}
if req.policy_type not in policy_coverage:
raise HTTPException(status_code=404, detail="Policy type not found")
return CoverageResponse(
policy_type=req.policy_type,
coverage=policy_coverage[req.policy_type]["coverage"],
premium=policy_coverage[req.policy_type]["premium"],
)
# Initiate policy coverage
@app.post("/policy/initiate")
async def initiate_policy(policy_request: PolicyInitiateRequest):
"""
Initiate policy coverage for a car, boat, house, or motorcycle.
"""
if policy_request.policy_type not in ["car", "boat", "house", "motorcycle"]:
raise HTTPException(status_code=400, detail="Invalid policy type")
return {
"message": f"Policy initiated for {policy_request.policy_type}",
"deductible": policy_request.deductible,
}
# Update claim details
@app.post("/policy/claim")
async def update_claim(req: ClaimUpdate):
"""
Update the status or details of a claim.
"""
# For simplicity, this is a mock update response
return {
"message": f"Claim {claim_update.claim_id} for policy {claim_update.claim_id} has been updated",
"update": claim_update.notes,
}
# Update deductible amount
@app.post("/policy/deductible")
async def update_deductible(deductible_update: DeductibleUpdate):
"""
Update the deductible amount for a specific policy.
"""
# For simplicity, this is a mock update response
return {
"message": f"Deductible for policy {deductible_update.policy_id} has been updated",
"new_deductible": deductible_update.deductible,
}
# Post method for policy Q/A
@app.post("/policy/qa")
async def policy_qa(conversation: Conversation):
"""
This method handles Q/A related to general issues in insurance.
It forwards the conversation to the OpenAI client via a local proxy and returns the response.
"""
return {
"choices": [
{
"message": {
"role": "assistant",
"content": "I am a helpful insurance agent, and can only help with insurance things",
},
"finish_reason": "completed",
"index": 0,
}
],
"model": "insurance_agent",
"usage": {"completion_tokens": 0},
}
# Run the app using:
# uvicorn main:app --reload

View file

@ -1,4 +0,0 @@
fastapi
uvicorn
pydantic
openai

View file

@ -1,12 +0,0 @@
apiVersion: 1
providers:
- name: "Dashboard provider"
orgId: 1
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: false
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: true

View file

@ -1,355 +0,0 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "code",
"expr": "avg(rate(envoy_cluster_internal_upstream_rq_time_sum[1m]) / rate(envoy_cluster_internal_upstream_rq_time_count[1m])) by (envoy_cluster_name)",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "request latency - internal (ms)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "code",
"expr": "avg(rate(envoy_cluster_external_upstream_rq_time_sum[1m]) / rate(envoy_cluster_external_upstream_rq_time_count[1m])) by (envoy_cluster_name)",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "request latency - external (ms)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "code",
"expr": "avg(rate(envoy_cluster_internal_upstream_rq_completed[1m])) by (envoy_cluster_name)",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "code",
"expr": "avg(rate(envoy_cluster_external_upstream_rq_completed[1m])) by (envoy_cluster_name)",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "B",
"useBackend": false
}
],
"title": "Upstream request count",
"type": "timeseries"
}
],
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Intelligent Gateway Overview",
"uid": "adt6uhx5lk8aob",
"version": 3,
"weekStart": ""
}

View file

@ -1,9 +0,0 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
access: proxy
editable: true

View file

@ -1,253 +0,0 @@
import logging
import random
import re
import sqlite3
from datetime import datetime, timedelta, timezone
import pandas as pd
from dateparser import parse
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def loadsql():
# Example Usage
conn = sqlite3.connect(":memory:")
# create and load the devices table
device_data = generate_device_data(conn)
# create and load the interface_stats table
generate_interface_stats_data(conn, device_data)
# create and load the flow table
generate_flow_data(conn, device_data)
return conn
# Function to convert natural language time expressions to "X {time} ago" format
def convert_to_ago_format(expression):
# Define patterns for different time units
time_units = {
r"seconds": "seconds",
r"minutes": "minutes",
r"mins": "mins",
r"hrs": "hrs",
r"hours": "hours",
r"hour": "hour",
r"hr": "hour",
r"days": "days",
r"day": "day",
r"weeks": "weeks",
r"week": "week",
r"months": "months",
r"month": "month",
r"years": "years",
r"yrs": "years",
r"year": "year",
r"yr": "year",
}
# Iterate over each time unit and create regex for each phrase format
for pattern, unit in time_units.items():
# Handle "for the past X {unit}"
match = re.search(rf"(\d+) {pattern}", expression)
if match:
quantity = match.group(1)
return f"{quantity} {unit} ago"
# If the format is not recognized, return None or raise an error
return None
# Function to generate random MAC addresses
def random_mac():
return "AA:BB:CC:DD:EE:" + ":".join(
[f"{random.randint(0, 255):02X}" for _ in range(2)]
)
# Function to generate random IP addresses
def random_ip():
return f"""{random.randint(1, 255)}
.{random.randint(1, 255)}
.{random.randint(1, 255)}
.{random.randint(1, 255)}"""
# Generate synthetic data for the device table
def generate_device_data(
conn,
n=1000,
):
device_data = {
"switchip": [random_ip() for _ in range(n)],
"hwsku": [f"HW{i+1}" for i in range(n)],
"hostname": [f"switch{i+1}" for i in range(n)],
"osversion": [f"v{i+1}" for i in range(n)],
"layer": ["L2" if i % 2 == 0 else "L3" for i in range(n)],
"region": [random.choice(["US", "EU", "ASIA"]) for _ in range(n)],
"uptime": [
f"""{random.randint(0, 10)} days {random.randint(0, 23)}
:{random.randint(0, 59)}:{random.randint(0, 59)}"""
for _ in range(n)
],
"device_mac_address": [random_mac() for _ in range(n)],
}
df = pd.DataFrame(device_data)
df.to_sql("device", conn, index=False)
return df
# Generate synthetic data for the interfacestats table
def generate_interface_stats_data(conn, device_df, n=1000):
interface_stats_data = []
for _ in range(n):
device_mac = random.choice(device_df["device_mac_address"])
ifname = random.choice(["eth0", "eth1", "eth2", "eth3"])
time = datetime.now(timezone.utc) - timedelta(
minutes=random.randint(0, 1440 * 5)
) # random timestamps in the past 5 day
in_discards = random.randint(0, 1000)
in_errors = random.randint(0, 500)
out_discards = random.randint(0, 800)
out_errors = random.randint(0, 400)
in_octets = random.randint(1000, 100000)
out_octets = random.randint(1000, 100000)
interface_stats_data.append(
{
"device_mac_address": device_mac,
"ifname": ifname,
"time": time,
"in_discards": in_discards,
"in_errors": in_errors,
"out_discards": out_discards,
"out_errors": out_errors,
"in_octets": in_octets,
"out_octets": out_octets,
}
)
df = pd.DataFrame(interface_stats_data)
df.to_sql("interfacestats", conn, index=False)
# Generate synthetic data for the ts_flow table
def generate_flow_data(conn, device_df, n=1000):
flow_data = []
for _ in range(n):
sampler_address = random.choice(device_df["switchip"])
proto = random.choice(["TCP", "UDP"])
src_addr = random_ip()
dst_addr = random_ip()
src_port = random.randint(1024, 65535)
dst_port = random.randint(1024, 65535)
in_if = random.randint(1, 10)
out_if = random.randint(1, 10)
flow_start = int(
(datetime.now() - timedelta(days=random.randint(1, 30))).timestamp()
)
flow_end = int(
(datetime.now() - timedelta(days=random.randint(1, 30))).timestamp()
)
bytes_transferred = random.randint(1000, 100000)
packets = random.randint(1, 1000)
flow_time = datetime.now(timezone.utc) - timedelta(
minutes=random.randint(0, 1440 * 5)
) # random flow time
flow_data.append(
{
"sampler_address": sampler_address,
"proto": proto,
"src_addr": src_addr,
"dst_addr": dst_addr,
"src_port": src_port,
"dst_port": dst_port,
"in_if": in_if,
"out_if": out_if,
"flow_start": flow_start,
"flow_end": flow_end,
"bytes": bytes_transferred,
"packets": packets,
"time": flow_time,
}
)
df = pd.DataFrame(flow_data)
df.to_sql("ts_flow", conn, index=False)
def load_params(req):
# Step 1: Convert the from_time natural language string to a timestamp if provided
if req.from_time:
# Use `dateparser` to parse natural language timeframes
logger.info("%s\n\nCaptured from time: %s\n\n", "* " * 50, req.from_time)
parsed_time = parse(req.from_time, settings={"RELATIVE_BASE": datetime.now()})
if not parsed_time:
conv_time = convert_to_ago_format(req.from_time)
if conv_time:
parsed_time = parse(
conv_time, settings={"RELATIVE_BASE": datetime.now()}
)
else:
return {
"error": """Invalid from_time format. Please provide a valid time description
such as 'past 7 days' or 'since last month'."""
}
logger.info("\n\nConverted from time: %s\n\n%s\n\n", parsed_time, "* " * 50)
from_time = parsed_time
logger.info("Using parsed from_time: %f", from_time)
else:
# If no from_time is provided, use a default value (e.g., the past 7 days)
from_time = datetime.now() - timedelta(days=7)
logger.info("Using default from_time: %f", from_time)
# Step 2: Build the dynamic SQL query based on the optional filters
filters = []
params = {"from_time": from_time}
if req.ifname:
filters.append("i.ifname = :ifname")
params["ifname"] = req.ifname
if req.region:
filters.append("d.region = :region")
params["region"] = req.region
if req.min_in_errors is not None:
filters.append("i.in_errors >= :min_in_errors")
params["min_in_errors"] = req.min_in_errors
if req.max_in_errors is not None:
filters.append("i.in_errors <= :max_in_errors")
params["max_in_errors"] = req.max_in_errors
if req.min_out_errors is not None:
filters.append("i.out_errors >= :min_out_errors")
params["min_out_errors"] = req.min_out_errors
if req.max_out_errors is not None:
filters.append("i.out_errors <= :max_out_errors")
params["max_out_errors"] = req.max_out_errors
if req.min_in_discards is not None:
filters.append("i.in_discards >= :min_in_discards")
params["min_in_discards"] = req.min_in_discards
if req.max_in_discards is not None:
filters.append("i.in_discards <= :max_in_discards")
params["max_in_discards"] = req.max_in_discards
if req.min_out_discards is not None:
filters.append("i.out_discards >= :min_out_discards")
params["min_out_discards"] = req.min_out_discards
if req.max_out_discards is not None:
filters.append("i.out_discards <= :max_out_discards")
params["max_out_discards"] = req.max_out_discards
return params, filters

View file

@ -0,0 +1,18 @@
# Stage 1: Build the application using Maven
FROM maven:3.8.7-openjdk-18-slim AS build
WORKDIR /app
# Copy pom.xml and download dependencies first (caching)
COPY pom.xml .
RUN mvn dependency:go-offline
# Copy the source code and build the application
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2: Run the application using a slim JDK image
FROM openjdk:17-jdk-slim
WORKDIR /app
# Copy the built jar from the previous stage
COPY --from=build /app/target/weather-forecast-service-0.0.1-SNAPSHOT.jar app.jar
# Expose the port on which the app runs (default Spring Boot is 8080)
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "app.jar"]

View file

@ -0,0 +1,45 @@
version: v0.1
listener:
address: 127.0.0.1
port: 10000 #If you configure port 443, you'll need to update the listener with tls_certificates
message_format: huggingface
# Centralized way to manage LLMs, manage keys, retry logic, failover and limits in a central way
llm_providers:
- name: OpenAI
provider_interface: openai
access_key: $OPENAI_API_KEY
model: gpt-4o-mini
default: true
# Arch creates a round-robin load balancing between different endpoints, managed via the cluster subsystem.
endpoints:
weather_forecast_service:
# value could be ip address or a hostname with port
# this could also be a list of endpoints for load balancing
# for example endpoint: [ ip1:port, ip2:port ]
endpoint: host.docker.internal:18081
# max time to wait for a connection to be established
connect_timeout: 0.005s
# default system prompt used by all prompt targets
system_prompt: |
You are a helpful weather assistant.
prompt_targets:
- name: weather_forecast
description: get the weather forecast
parameters:
- name: location
description: the location for which to get the weather forecast
required: true
type: string
format: City, State
- name: days
description: the number of days for the forecast
required: true
type: int
endpoint:
name: weather_forecast_service
path: /weather
http_method: POST

View file

@ -1,18 +1,14 @@
services:
api_server:
weather_forecast_service:
build:
context: .
dockerfile: Dockerfile
ports:
- "18083:80"
healthcheck:
test: ["CMD", "curl" ,"http://localhost:80/healthz"]
interval: 5s
retries: 20
- "18081:8081"
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"

View file

@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>weather</groupId>
<artifactId>weather-forecast-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,12 @@
// File: src/main/java/com/example/weather/WeatherForecastApplication.java
package weather;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WeatherForecastApplication {
public static void main(String[] args) {
SpringApplication.run(WeatherForecastApplication.class, args);
}
}

View file

@ -0,0 +1,54 @@
package weather.controller;
import weather.model.DayForecast;
import weather.model.WeatherForecastResponse;
import weather.model.WeatherRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RestController
public class WeatherController {
private Random random = new Random();
@PostMapping("/weather")
public WeatherForecastResponse getRandomWeatherForecast(@RequestBody WeatherRequest req) {
WeatherForecastResponse response = new WeatherForecastResponse();
response.setLocation(req.getLocation());
response.setUnits(req.getUnits());
List<DayForecast> forecasts = new ArrayList<>();
for (int i = 0; i < req.getDays(); i++) {
// Generate a random min temperature between 50 and 89 (inclusive)
int minTemp = random.nextInt(90 - 50) + 50;
// Generate a max temperature between (minTemp + 5) and (minTemp + 19)
int maxTemp = random.nextInt(15) + (minTemp + 5);
double finalMinTemp = minTemp;
double finalMaxTemp = maxTemp;
// Convert to Celsius if necessary
if (req.getUnits().equalsIgnoreCase("celsius") || req.getUnits().equalsIgnoreCase("c")) {
finalMinTemp = (minTemp - 32) * 5.0 / 9.0;
finalMaxTemp = (maxTemp - 32) * 5.0 / 9.0;
}
DayForecast dayForecast = new DayForecast();
dayForecast.setDate(LocalDate.now().plusDays(i).toString());
dayForecast.setMin(finalMinTemp);
dayForecast.setMax(finalMaxTemp);
dayForecast.setUnits(req.getUnits());
forecasts.add(dayForecast);
}
response.setDailyForecast(forecasts);
return response;
}
}

View file

@ -0,0 +1,40 @@
package weather.model;
public class DayForecast {
private String date;
private String units;
private double min;
private double max;
public DayForecast() {}
// Getters and setters
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getUnits() {
return units;
}
public void setUnits(String units) {
this.units = units;
}
public double getMin() {
return min;
}
public void setMin(double min) {
this.min = min;
}
public double getMax() {
return max;
}
public void setMax(double max) {
this.max = max;
}
}

View file

@ -0,0 +1,37 @@
package weather.model;
import java.util.List;
public class WeatherForecastResponse {
private String location;
private String units;
private List<DayForecast> forecast;
// Default Constructor
public WeatherForecastResponse() {}
// Getters and Setters
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getUnits() {
return units;
}
public void setUnits(String units) {
this.units = units;
}
public List<DayForecast> getDailyForecast() {
return forecast;
}
public void setDailyForecast(List<DayForecast> forecast) {
this.forecast = forecast;
}
}

View file

@ -0,0 +1,29 @@
package weather.model;
public class WeatherRequest {
private String location;
private int days = 7;
private String units = "Farenheit";
public WeatherRequest() {}
// Getters and setters
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public String getUnits() {
return units;
}
public void setUnits(String units) {
this.units = units;
}
}

View file

@ -1,7 +1,7 @@
services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -14,7 +14,7 @@ services:
jaeger:
build:
context: ../shared/jaeger
context: ../../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"

View file

@ -8,7 +8,6 @@ services:
- CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:10000/v1
volumes:
- ./arch_config.yaml:/app/arch_config.yaml
- ../shared/chatbot_ui/common.py:/app/common.py
ports:
- "18080:80"
healthcheck:
@ -18,7 +17,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"

View file

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 549 KiB

Before After
Before After

View file

@ -12,7 +12,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"

View file

Before

Width:  |  Height:  |  Size: 852 KiB

After

Width:  |  Height:  |  Size: 852 KiB

Before After
Before After

View file

@ -7,13 +7,13 @@ WORKDIR /src
COPY requirements.txt /src/
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
COPY . /src
COPY ../. /src
FROM python:3.12-slim AS output
COPY --from=builder /runtime /usr/local
COPY . /app
COPY ../. /app
WORKDIR /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--log-level", "info"]

View file

@ -8,7 +8,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"

View file

Before

Width:  |  Height:  |  Size: 636 KiB

After

Width:  |  Height:  |  Size: 636 KiB

Before After
Before After

View file

@ -24,6 +24,7 @@ start_demo() {
# Step 4: Start Network Agent
echo "Starting Network Agent using Docker Compose..."
cd build
docker compose up -d # Run in detached mode
}

View file

@ -1,7 +1,7 @@
services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -14,7 +14,7 @@ services:
jaeger:
build:
context: ../shared/jaeger
context: ../../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"

View file

Before

Width:  |  Height:  |  Size: 673 KiB

After

Width:  |  Height:  |  Size: 673 KiB

Before After
Before After

View file

@ -11,7 +11,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -24,12 +24,12 @@ services:
otel-collector:
build:
context: ../shared/honeycomb/
context: ../../shared/honeycomb/
ports:
- "4317:4317"
- "4318:4318"
volumes:
- ../shared/honeycomb/otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ../../shared/honeycomb/otel-collector-config.yaml:/etc/otel-collector-config.yaml
env_file:
- .env
environment:
@ -37,10 +37,10 @@ services:
prometheus:
build:
context: ../shared/prometheus
context: ../../shared/prometheus
grafana:
build:
context: ../shared/grafana
context: ../../shared/grafana
ports:
- "3000:3000"

View file

@ -11,7 +11,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -24,7 +24,7 @@ services:
jaeger:
build:
context: ../shared/jaeger
context: ../../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"
@ -32,10 +32,10 @@ services:
prometheus:
build:
context: ../shared/prometheus
context: ../../shared/prometheus
grafana:
build:
context: ../shared/grafana
context: ../../shared/grafana
ports:
- "3000:3000"

View file

@ -11,7 +11,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -24,12 +24,12 @@ services:
otel-collector:
build:
context: ../shared/logfire/
context: ../../shared/logfire/
ports:
- "4317:4317"
- "4318:4318"
volumes:
- ../shared/logfire/otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ../../shared/logfire/otel-collector-config.yaml:/etc/otel-collector-config.yaml
env_file:
- .env
environment:
@ -37,10 +37,10 @@ services:
prometheus:
build:
context: ../shared/prometheus
context: ../../shared/prometheus
grafana:
build:
context: ../shared/grafana
context: ../../shared/grafana
ports:
- "3000:3000"

View file

@ -1,5 +1,5 @@
include:
- ../shared/signoz/docker-compose-minimal.yaml
- ../../shared/signoz/docker-compose-minimal.yaml
services:
weather_forecast_service:
@ -14,7 +14,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -27,10 +27,10 @@ services:
prometheus:
build:
context: ../shared/prometheus
context: ../../shared/prometheus
grafana:
build:
context: ../shared/grafana
context: ../../shared/grafana
ports:
- "3000:3000"

View file

@ -11,7 +11,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -19,23 +19,3 @@ services:
- CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:10000/v1
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./arch_config.yaml:/app/arch_config.yaml
jaeger:
build:
context: ../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"
- "4318:4318"
prometheus:
build:
context: ../shared/prometheus
grafana:
build:
context: ../shared/grafana
ports:
- "3000:3000"

View file

@ -7,13 +7,13 @@ do
echo "******************************************"
echo "Running tests for $demo ..."
echo "****************************************"
cd ../$demo
cd ../../samples_python/$demo
archgw up arch_config.yaml
docker compose up -d
cd ../test_runner
TEST_DATA=../$demo/test_data.yaml poetry run pytest
cd ../$demo
cd ../../shared/test_runner
TEST_DATA=../../samples_python/$demo/test_data.yaml poetry run pytest
cd ../../samples_python/$demo
archgw down
docker compose down -v
cd ../test_runner
cd ../../shared/test_runner
done

View file

@ -1,5 +1,5 @@
# LLM Routing
This demo shows how you can arch gateway to manage keys and route to appropriate LLM.
This demo shows how you can arch gateway to manage keys and route to upstream LLM.
# Starting the demo
1. Please make sure the [pre-requisites](https://github.com/katanemo/arch/?tab=readme-ov-file#prerequisites) are installed correctly

View file

@ -2,7 +2,7 @@ services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"
@ -15,7 +15,7 @@ services:
jaeger:
build:
context: ../shared/jaeger
context: ../../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"
@ -23,10 +23,10 @@ services:
prometheus:
build:
context: ../shared/prometheus
context: ../../shared/prometheus
grafana:
build:
context: ../shared/grafana
context: ../../shared/grafana
ports:
- "3000:3000"

View file

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 273 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 284 KiB

Before After
Before After

View file

@ -1,7 +1,7 @@
services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -14,7 +14,7 @@ services:
jaeger:
build:
context: ../shared/jaeger
context: ../../shared/jaeger
ports:
- "16686:16686"
- "4317:4317"

View file

@ -1,7 +1,7 @@
services:
chatbot_ui:
build:
context: ../shared/chatbot_ui
context: ../../shared/chatbot_ui
ports:
- "18080:8080"
environment:
@ -14,12 +14,12 @@ services:
otel-collector:
build:
context: ../shared/honeycomb/
context: ../../shared/honeycomb/
ports:
- "4317:4317"
- "4318:4318"
volumes:
- ../shared/honeycomb/otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ../../shared/honeycomb/otel-collector-config.yaml:/etc/otel-collector-config.yaml
env_file:
- .env
environment:

View file

@ -22,7 +22,7 @@ start_demo() {
echo "Starting Arch with arch_config.yaml..."
archgw up arch_config.yaml
# Step 4: Start Network Agent
# Step 4: Start developer services
echo "Starting Network Agent using Docker Compose..."
docker compose up -d # Run in detached mode
}

View file

@ -80,7 +80,7 @@ Once the prompt targets are configured as above, handle parameters across multi-
Demo App
~~~~~~~~
For your convenience, we've built a `demo app <https://github.com/katanemo/archgw/main/demos/multi_turn_rag_agent>`_
For your convenience, we've built a `demo app <https://github.com/katanemo/archgw/tree/main/demos/samples_python/multi_turn_rag_agent>`_
that you can test and modify locally for multi-turn RAG scenarios.
.. figure:: includes/multi_turn/mutli-turn-example.png

View file

@ -8,7 +8,7 @@ services:
- "12000:12000"
- "19901:9901"
volumes:
- ../../demos/weather_forecast/arch_config.yaml:/app/arch_config.yaml
- ../../demos/samples_python/weather_forecast/arch_config.yaml:/app/arch_config.yaml
- /etc/ssl/cert.pem:/etc/ssl/cert.pem
- ~/archgw_logs:/var/log/
extra_hosts:

View file

@ -26,7 +26,7 @@ log starting > ../build.log
log building and running function_callling demo
log ===========================================
cd ../../demos/weather_forecast/
cd ../../demos/samples_python/weather_forecast/
docker compose up weather_forecast_service --build -d
cd -
@ -53,7 +53,7 @@ cd ../../
tail -F ~/archgw_logs/modelserver.log &
model_server_tail_pid=$!
archgw down
archgw up demos/weather_forecast/arch_config.yaml
archgw up demos/samples_python/weather_forecast/arch_config.yaml
kill $model_server_tail_pid
cd -
@ -68,6 +68,6 @@ archgw down
log shutting down the weather_forecast demo
log =======================================
cd ../../demos/weather_forecast
cd ../../demos/samples_python/weather_forecast
docker compose down
cd -