Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2a0db39
updated prompts and tests
Christopher-Stevers Oct 27, 2025
83423d9
remove unused changes
Christopher-Stevers Oct 27, 2025
2910ec6
Merge branch 'master' of https://github.com/geistlabs/geistai
Christopher-Stevers Oct 28, 2025
1d99e51
refactored storage system to always use enhanced messages
Christopher-Stevers Oct 28, 2025
3f7740b
fixed cited text not rendering as markdown
Christopher-Stevers Oct 29, 2025
8bc6a3f
fixed storage not storing correct format of message
Christopher-Stevers Oct 29, 2025
93c6acc
now saves chat tool calls
Christopher-Stevers Oct 29, 2025
89c5306
updated testing system and started using gemini for it
Christopher-Stevers Oct 29, 2025
8f0b83e
updated boot config
Christopher-Stevers Oct 29, 2025
3c63340
recreated remote tests
Christopher-Stevers Oct 31, 2025
c7ca000
add cloud test
Christopher-Stevers Oct 31, 2025
78e2842
give cloud test a longer time to wait for inference and rename inference
Christopher-Stevers Oct 31, 2025
4a96b77
give cloud test a longer time to wait for inference and rename inference
Christopher-Stevers Oct 31, 2025
f0bef2d
increased timeout
Christopher-Stevers Oct 31, 2025
29294fd
add proper viewing of log
Christopher-Stevers Oct 31, 2025
56f2b4d
add dep
Christopher-Stevers Oct 31, 2025
7233c94
use rating inference key from env
Christopher-Stevers Oct 31, 2025
fcbfc90
use rating inference key from env
Christopher-Stevers Oct 31, 2025
787b9a7
log api url
Christopher-Stevers Oct 31, 2025
31bf8c5
log url without exposing secret
Christopher-Stevers Oct 31, 2025
a31e5fd
fixed setting wrong remote inference model
Christopher-Stevers Oct 31, 2025
0884489
log out request body
Christopher-Stevers Oct 31, 2025
d650e05
log out request response
Christopher-Stevers Oct 31, 2025
dc77e45
updated to load env vars more gracefully in test
Christopher-Stevers Oct 31, 2025
206786f
fixed gemini base reasonablness test
Christopher-Stevers Oct 31, 2025
02e5ce5
add proper testing
Christopher-Stevers Nov 1, 2025
f623f73
add github actions
Christopher-Stevers Nov 2, 2025
95fde0d
add proper auth to github actions
Christopher-Stevers Nov 2, 2025
28874f7
updated service account config
Christopher-Stevers Nov 2, 2025
c54ae8f
removed unconfigured line
Christopher-Stevers Nov 2, 2025
21bf1a3
removed psycog
Christopher-Stevers Nov 2, 2025
d9a766c
remove excess dep installs
Christopher-Stevers Nov 2, 2025
98f604f
move authenticate with gcp to start
Christopher-Stevers Nov 2, 2025
3d28a90
add missing router
Christopher-Stevers Nov 2, 2025
051fd1b
skip saving test results in prod
Christopher-Stevers Nov 2, 2025
2e79bbc
optimized prompts
Christopher-Stevers Nov 3, 2025
ebd7426
merged upstream
Christopher-Stevers Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .github/workflows/cloud-test-router.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: Test Router Services with Cloud SQL

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test-router:
# 1. REQUIRED: Set permissions to get the GitHub OIDC token
permissions:
contents: 'read'
id-token: 'write' # Grants permission to fetch the OIDC token for WIF

runs-on: ubuntu-latest

# Global environment variables for the job
env:
# Application path setup
PYTHONPATH: ${{ github.workspace }}/backend:${{ github.workspace }}/backend/router:${{ github.workspace }}/backend/embeddings:${{ github.workspace }}/backend/database

# *** WIF & Cloud SQL IAM ENV VARS ***
SERVICE_ACCOUNT_EMAIL: ${{ secrets.GCP_SERVICE_ACCOUNT }}
INSTANCE_CONNECTION_NAME: ${{ secrets.INSTANCE_CONNECTION_NAME }}

# Construct the DATABASE_URL for IAM Auth: user is the SA email, password is empty, host is 127.0.0.1 (proxy)
DATABASE_URL: postgresql://${{ secrets.GCP_SERVICE_ACCOUNT }}:@127.0.0.1:5432/${{ secrets.DB_NAME }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Cache pip dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('backend/**/pyproject.toml', 'backend/**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-



# 5. AUTHENTICATE TO GOOGLE CLOUD VIA WIF
- name: 'Authenticate to Google Cloud'
id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
# Credentials are now set as Application Default Credentials (ADC)

# 6. START CLOUD SQL PROXY USING IAM AUTH
- name: 'Start Cloud SQL Proxy'
run: |
wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64 -O cloud-sql-proxy
chmod +x cloud-sql-proxy
# --auto-iam-authn uses the ADC established by the 'Authenticate' step
./cloud-sql-proxy --auto-iam-authn ${{ env.INSTANCE_CONNECTION_NAME }} &
echo $! > cloud-sql-proxy.pid
sleep 5 # Wait for the proxy to initialize

- name: Create test environment file
working-directory: ./backend
run: |
cat > .env << EOF
RATING_INFERENCE_KEY=${{ secrets.RATING_INFERENCE_KEY }}
INFERENCE_URL=https://inference.geist.im
EMBEDDINGS_URL=https://embeddings.geist.im
HARMONY_ENABLED=false
LOG_LEVEL=INFO
# Use the WIF-constructed DATABASE_URL from the env block
DATABASE_URL=${{ env.DATABASE_URL }}
EOF


- name: Install Python dependencies
working-directory: ./backend
run: |
python -m pip install --upgrade pip
pip install fastapi httpx uvicorn sse-starlette python-multipart python-dotenv
pip install pytest pytest-asyncio sentence_transformers sqlalchemy alembic python-dateutil psycopg2

- name: Start router service
working-directory: ./backend/router
run: |
python main.py > router.log 2>&1 &
echo $! > router.pid
sleep 10
env:
SKIP_TEST_SAVING: true
RATING_INFERENCE_KEY: ${{ secrets.RATING_INFERENCE_KEY }}
USE_REMOTE_INFERENCE: false
INFERENCE_URL: https://inference.geist.im
EMBEDDINGS_URL: https://embeddings.geist.im
LOG_LEVEL: INFO
# Note: App should read DATABASE_URL from .env file created above

- name: Debug router log
run: cat backend/router/router.log || true

- name: Wait for router to be ready
run: |
timeout 60 bash -c 'until curl -f http://localhost:8000/health; do sleep 2; done'

- name: Run streaming tests (test_conversation.py)
working-directory: ./backend/router
run: |
echo "=== Starting streaming tests ==="
python test_conversation.py
echo "=== Streaming tests completed ==="
# No need to explicitly pass DATABASE_URL here if the test also reads the .env file

- name: Run health check tests
working-directory: ./backend/router
run: |
python test_health_endpoint.py

- name: Save router logs
if: always()
working-directory: ./backend/router
run: |
echo "=== Router Logs ==="
cat router.log || echo "No router.log found"
echo "=== End Router Logs ==="

- name: Cleanup
if: always()
run: |
# Cleanup router service
if [ -f backend/router/router.pid ]; then
kill $(cat backend/router/router.pid) 2>/dev/null || true
rm -f backend/router/router.pid
fi
pkill -f "python main.py" || true

# Cleanup Cloud SQL Proxy
if [ -f cloud-sql-proxy.pid ]; then
kill $(cat cloud-sql-proxy.pid) 2>/dev/null || true
rm -f cloud-sql-proxy.pid
fi
15 changes: 15 additions & 0 deletions backend/database/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import subprocess
import logging
from pathlib import Path
from dotenv import load_dotenv

# Add the backend directory to the Python path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Expand All @@ -24,13 +25,27 @@

def run_alembic_command(command: str, *args):
"""Run an alembic command with proper environment setup"""
try:
# Get the directory where this config.py file is located
env_file = Path(__file__).parent
# Go up one directory to find the .env file
env_file = env_file.parent / ".env"
print(f"Loading .env file from: {env_file}")
if env_file.exists():
load_dotenv(env_file)
print(f"Loaded environment variables from: {env_file}")
else:
print(f"No .env file found at: {env_file}")
except ImportError:
print("python-dotenv not installed, skipping .env file loading")
try:
# Set environment variables
env = os.environ.copy()
env['DATABASE_URL'] = os.getenv(
'DATABASE_URL',
'postgresql://postgres:password@localhost:5433/test-storage'
)
print(f"Using DATABASE_URL: {env['DATABASE_URL']}")
# Change to the database directory
db_dir = Path(__file__).parent
os.chdir(db_dir)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Add first_token_time and num_tool_calls columns to conversation_response

Revision ID: 0004
Revises: 0003
Create Date: 2024-06-09 00:00:00.000000

"""

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '0004'
down_revision = '0003'
branch_labels = None
depends_on = None

def upgrade() -> None:
# Add first_token_time (float) and num_tool_calls (integer) to conversation_response
op.add_column('conversation_response', sa.Column('first_token_time', sa.Float(), nullable=True))
op.add_column('conversation_response', sa.Column('num_tool_calls', sa.Integer(), nullable=True))

def downgrade() -> None:
# Remove the two columns in downgrade
op.drop_column('conversation_response', 'num_tool_calls')
op.drop_column('conversation_response', 'first_token_time')

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Add test_run_time to conversation_response

Revision ID: 6e6db6b65802
Revises: 0004
Create Date: 2025-10-27 10:31:49.902161

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '6e6db6b65802'
down_revision = '0004'
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('conversation_response', sa.Column('test_run_time', sa.DateTime(timezone=True), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('conversation_response', 'test_run_time')
# ### end Alembic commands ###


3 changes: 3 additions & 0 deletions backend/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class ConversationResponse(Base):
rationality = Column(Float, nullable=True) # Rationality score
coherency = Column(Float, nullable=True) # Coherency score
elapsed_time = Column(Float, nullable=True) # Response time in seconds
first_token_time = Column(Float, nullable=True) # Time to first token
num_tool_calls = Column(Integer, nullable=True) # Number of tool calls
test_run_time = Column(DateTime(timezone=True), nullable=True) # Timestamp for test suite iteration

# Foreign key to conversation (many responses belong to one conversation)
conversation_id = Column(Integer, ForeignKey('conversation.internal_id', ondelete='CASCADE'), nullable=True)
Expand Down
13 changes: 7 additions & 6 deletions backend/docker-compose.chris.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
build: ./router
ports:
- "0.0.0.0:8000:8000" # Bind to all interfaces
- "0.0.0.0:8443:8443"# HTTPS port (uncomment if using SSL)
- "0.0.0.0:8443:8443" # HTTPS port (uncomment if using SSL)
environment:
- LOG_LEVEL=DEBUG
- HARMONY_REASONING_EFFORT=low
Expand All @@ -14,12 +14,13 @@ services:
- PYTHONUNBUFFERED=1
- PYTHONDONTWRITEBYTECODE=1
- WATCHDOG_POLLING=true

- OPENAI_URL=https://api.openai.com
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- OPENAI_KEY=${OPENAI_KEY}
- RATING_INFERENCE_URL=${RATING_INFERENCE_URL}
- RATING_INFERENCE_KEY=${RATING_INFERENCE_KEY}
- RATING_INFERENCE_MODEL=${RATING_INFERENCE_MODEL}
- REMOTE_INFERENCE_KEY=${REMOTE_INFERENCE_KEY}
- USE_REMOTE_INFERENCE=true
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- REMOTE_INFERENCE_MODEL=${REMOTE_INFERENCE_MODEL}
- REMOTE_INFERENCE_URL=${REMOTE_INFERENCE_URL}
- BRAVE_API_KEY=${BRAVE_API_KEY}
- MCP_BRAVE_URL=http://mcp-brave:8080
- MCP_FETCH_URL=http://mcp-fetch:8000
Expand Down
30 changes: 15 additions & 15 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
build: ./router
ports:
- "0.0.0.0:8000:8000" # Bind to all interfaces
- "0.0.0.0:8443:8443"# HTTPS port (uncomment if using SSL)
- "0.0.0.0:8443:8443" # HTTPS port (uncomment if using SSL)
environment:
- LOG_LEVEL=DEBUG
- HARMONY_REASONING_EFFORT=low
Expand All @@ -17,12 +17,13 @@ services:
- PYTHONUNBUFFERED=1
- PYTHONDONTWRITEBYTECODE=1
- WATCHDOG_POLLING=true
- REMOTE_INFERENCE_URL=https://api.studio.nebius.com
- RATING_INFERENCE_URL=https://api.openai.com
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- OPENAI_KEY=${OPENAI_KEY}
- RATING_INFERENCE_URL=${RATING_INFERENCE_URL}
- RATING_INFERENCE_KEY=${RATING_INFERENCE_KEY}
- RATING_INFERENCE_MODEL=${RATING_INFERENCE_MODEL}
- REMOTE_INFERENCE_KEY=${REMOTE_INFERENCE_KEY}
- USE_REMOTE_INFERENCE=true
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- REMOTE_INFERENCE_MODEL=${REMOTE_INFERENCE_MODEL}
- REMOTE_INFERENCE_URL=${REMOTE_INFERENCE_URL}
- BRAVE_API_KEY=${BRAVE_API_KEY}
- MCP_BRAVE_URL=http://mcp-brave:8080

Expand Down Expand Up @@ -63,7 +64,7 @@ services:
build: ./router
ports:
- "8000:8000" # Bind to all interfaces
- "8443:8443"# HTTPS port (uncomment if using SSL)
- "8443:8443" # HTTPS port (uncomment if using SSL)
environment:
- LOG_LEVEL=DEBUG
- HARMONY_REASONING_EFFORT=low
Expand All @@ -75,11 +76,6 @@ services:
- PYTHONDONTWRITEBYTECODE=1
- WATCHDOG_POLLING=true
- MCP_BRAVE_URL=http://mcp-brave:8080
- OPENAI_URL=https://api.openai.com
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- OPENAI_KEY=${OPENAI_KEY}
- USE_REMOTE_INFERENCE=true
- BRAVE_API_KEY=${BRAVE_API_KEY}

volumes:
# Mount source code for live reloading
Expand Down Expand Up @@ -132,9 +128,13 @@ services:
- DISABLE_PREMIUM_CHECK=true
- WATCHDOG_POLLING=true
- MCP_BRAVE_URL=http://mcp-brave:8080
- OPENAI_URL=https://api.openai.com
- USE_REMOTE_INFERENCE=false
- OPENAI_KEY=${OPENAI_KEY}
- RATING_INFERENCE_URL=${RATING_INFERENCE_URL}
- RATING_INFERENCE_KEY=${RATING_INFERENCE_KEY}
- RATING_INFERENCE_MODEL=${RATING_INFERENCE_MODEL}
- REMOTE_INFERENCE_KEY=${REMOTE_INFERENCE_KEY}
- USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
- REMOTE_INFERENCE_MODEL=${REMOTE_INFERENCE_MODEL}
- REMOTE_INFERENCE_URL=${REMOTE_INFERENCE_URL}
- BRAVE_API_KEY=${BRAVE_API_KEY}
- MCP_FETCH_URL=http://mcp-fetch:8000

Expand Down
17 changes: 10 additions & 7 deletions backend/env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# OpenAI Configuration
OPENAI_KEY=your-openai-api-key-here

# Brave api key
# Inference configuration
RATING_INFERENCE_URL=${RATING_INFERENCE_URL}
RATING_INFERENCE_KEY=${RATING_INFERENCE_KEY}
RATING_INFERENCE_MODEL=${RATING_INFERENCE_MODEL}
REMOTE_INFERENCE_KEY=${REMOTE_INFERENCE_KEY}
USE_REMOTE_INFERENCE=${USE_REMOTE_INFERENCE}
REMOTE_INFERENCE_MODEL=${REMOTE_INFERENCE_MODEL}
REMOTE_INFERENCE_URL=${REMOTE_INFERENCE_U
# Brapi key
BRAVE_API_KEY=your-brave-api-key-here

# Service URLs
Expand All @@ -16,7 +21,6 @@ REASONING_EFFORT=low
API_HOST=0.0.0.0
API_PORT=8000


# Timeouts
INFERENCE_TIMEOUT=60
EMBEDDINGS_TIMEOUT=60
Expand All @@ -27,8 +31,7 @@ LOG_LEVEL=INFO
# Tool Calling Configuration
ENABLE_TOOL_CALLS=true

# Remote Inference
USE_REMOTE_INFERENCE=true

ENABLE_TOOL_CALLS=false


Loading
Loading