Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions .github/workflows/code_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ jobs:
# has an issue with tests:
# https://github.com/streamlit/streamlit/issues/12566
# GHSA-5239-wwwm-4pmq: issue with pygments 2.19.12 that can't be upgraded because it breaks the docs build
# PYSEC-2026-2132: Click cannot be upgraded to 8.3.3 because of inspectai
ignore-vulns: |
GHSA-7p48-42j8-8846
GHSA-5239-wwwm-4pmq
PYSEC-2026-212
PYSEC-2026-2132
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ wheels/
# Virtual environments
.venv

# Spec / task skill local state
.specs_skill_state/

# Lint & Test
.mypy_cache/
.pytest_cache/
Expand Down
15 changes: 11 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ endpoint that takes a form data with the following fields:
- An `api_key` to access the models
- A file upload `systematized_concepts_file`, which can be downloaded after answering
all the questions from the main app.
- (Optional) The model to use for the evaluation. Default is `openai/gpt-4o`. Possible values
are `openai/gpt-4o`, `openai/gpt-5.5`, `openai/gpt-5.4-mini`, `google/gemini-3.1-pro-preview`,
`google/gemini-3-flash-preview`, `google/gemini-3.1-flash-lite`, `anthropic/claude-opus-4-7`,
`anthropic/claude-sonnet-4-6` and `anthropic/claude-haiku-4-5-20251001`.
- (Optional) The model to use for the evaluation. Default is `gpt-4o`. Possible values
are `gpt-4o`, `gpt-5.5`, `gpt-5.4-mini`, `gemini-3.1-pro-preview`,
`gemini-3-flash-preview`, `claude-opus-4-7`, and `claude-sonnet-4-6`.

To see the full documentation for the available endpoints, you can access
`http://localhost:8000/docs` on your browser.

### 🔑 LLM API keys (Vector proxy)

LLM calls go through Vector's OpenAI-compatible proxy at
`https://proxy.vectorinstitute.ai/v1`. Use a Vector proxy API key when prompted in the
UI/API (`api_key`). For local manual tests, set `ASPIS_API_KEY` or `OPENAI_API_KEY` to
that proxy key. Do not rely on writing provider keys into the process environment for
request auth.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,15 @@ endpoint that takes a form data with the following fields:
- An `api_key` to access the models
- A file upload `systematized_concepts_file`, which can be downloaded after answering
all the questions from the main app.
- (Optional) The model to use for the evaluation. Default is `openai/gpt-4o`. Possible values
are `openai/gpt-4o`, `openai/gpt-5.5`, `openai/gpt-5.4-mini`, `google/gemini-3.1-pro-preview`,
`google/gemini-3-flash-preview`, `google/gemini-3.1-flash-lite`, `anthropic/claude-opus-4-7`,
`anthropic/claude-sonnet-4-6` and `anthropic/claude-haiku-4-5-20251001`.
- (Optional) The model to use for the evaluation. Default is `gpt-4o`. Possible values
are `gpt-4o`, `gpt-5.5`, `gpt-5.4-mini`, `gemini-3.1-pro-preview`,
`gemini-3-flash-preview`, `claude-opus-4-7`, and `claude-sonnet-4-6`.

To see the full documentation for the available endpoints, you can access
`http://localhost:8080/api/docs` on your browser.

### 🔑 LLM API keys (Vector proxy)

LLM calls are routed through Vector's OpenAI-compatible proxy at
`https://proxy.vectorinstitute.ai/v1`. Pass a Vector proxy API key as `api_key` in the
UI or API (not a provider-specific key written into process environment variables).
71 changes: 71 additions & 0 deletions plans/2026-08-10-replace-inspectai-with-openai-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Plan: Replace InspectAI with OpenAI SDK via Vector Proxy

**Date:** 2026-08-10
**Branch:** `replace-inspectai-with-openai-proxy`
**Status:** complete

## Goal

Replace InspectAI-based LLM calls with the official OpenAI SDK, routed through `https://proxy.vectorinstitute.ai/v1`, while keeping multi-provider models and safe concurrent API-key handling.

## Decisions (from clarification)

1. **Multi-provider:** Keep Gemini/Claude/OpenAI models; the Vector proxy supports all of them via the OpenAI-compatible API.
2. **Auth:** Keep the existing pattern of passing `api_key` into call sites (UI/API). Do **not** write keys into process `os.environ` (unsafe under concurrency). Prefer per-call `OpenAI(api_key=..., base_url=...)` clients.
3. **Client:** Official `openai` SDK (not `langchain-openai`).
4. **Threading:** Remove `ThreadPoolExecutor` unless verification shows Streamlit still requires it. The old thread pool existed for InspectAI + Streamlit main-thread issues.
5. **Lockfile:** After `pyproject.toml` dependency changes, regenerate `uv.lock` with `uv` (e.g. `uv lock` / `uv sync`). **Never hand-edit `uv.lock`.**

## Steps

### 1. Branch

- Work on `replace-inspectai-with-openai-proxy` (created from `main`).

### 2. New LLM client layer (`inferencer.py` core)

- Add an OpenAI client factory that always creates a **new per-call client**:
- `OpenAI(api_key=<caller-provided key>, base_url="https://proxy.vectorinstitute.ai/v1")`
- Never set `os.environ` for keys.
- Replace InspectAI `Task` / `eval` / `generate` / `model_graded_qa` with `client.chat.completions.create(...)`.
- Replace InspectAI `Sample` inputs with prompt strings.
- Keep `ModelInfo` and multi-provider model IDs; send those model IDs to the proxy.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- Adapt `extract_string_output` to OpenAI response content shapes as needed.
- Drop `ThreadPoolExecutor` if direct sync OpenAI calls are safe with Streamlit.

### 3. Call sites / API surface

- Update `systematization.py`, API, UI, and helpers for the new sample/client path.
- Preserve “pass `api_key` into the function” from UI form + API form.

### 4. Dependencies

- Add official `openai` SDK.
- Remove `inspect-ai`.
- Remove now-unused provider SDKs only if nothing else imports them after the switch (`anthropic`, `google-genai` are candidates to audit).
- Regenerate `uv.lock` via `uv` from `pyproject.toml` changes (no manual lock edits).
- Update CI notes that mention InspectAI (e.g. click pin comment in code checks).

### 5. Tests

- Rewrite tests that mock `inspect_ai_eval` to mock the OpenAI client instead.
- Remove InspectAI imports from tests.
- Cover that API keys are passed into the client constructor (not env), so concurrent calls cannot share/override keys.

### 6. Docs / misc

- Light README/CONTRIBUTING updates if they describe InspectAI or key setup incorrectly.
- Leave `.env` alone (secrets); wire default base URL in code (optional non-secret env override if useful).

### 7. Quality gate

- Coding agent implements + runs project checks/tests.
- Separate code-review agent reviews.
- Commit only after a clean review.
- Verify clean `git status` and summarize.

## Non-goals

- No InspectAI proxy workaround.
- No shared global client / env-based key for request auth.
- No full project re-spec; this is a `/spec task`.
5 changes: 1 addition & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ authors = [ {name = "Vector AI Engineering", email = "ai_engineering@vectorinsti
license = "Apache-2.0"
requires-python = ">=3.12"
dependencies = [
"anthropic>=0.102.0",
"fastapi[standard]>=0.121.0",
"google-genai>=2.2.0",
"inspect-ai>=0.3.160",
"langchain>=1.3.9",
"langchain-openai>=1.1.14",
"openai>=1.40.0",
"pydantic>=2.11.7",
"pyyaml>=6.0.2",
"streamlit>=1.48.1",
Expand Down
13 changes: 7 additions & 6 deletions src/aspis/api/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Functions for the programmatic API of the Aspis application."""

import asyncio
import datetime
from typing import Any

Expand Down Expand Up @@ -45,11 +46,10 @@ async def evaluate(
text_to_evaluate: The text to evaluate.
api_key: The API key to use to connect to the model.
model: The model to use for this evaluation. Optional,
defaults to `openai/gpt-4o`. Allowed values are `openai/gpt-4o`,
`openai/gpt-5.5`, `openai/gpt-5.4-mini`, `google/gemini-3.1-pro-preview`,
`google/gemini-3-flash-preview`, `google/gemini-3.1-flash-lite`,
`anthropic/claude-opus-4-7`, `anthropic/claude-sonnet-4-6`
and `anthropic/claude-haiku-4-5-20251001`.
defaults to `gpt-4o`. Allowed values are `gpt-4o`,
`gpt-5.5`, `gpt-5.4-mini`, `gemini-3.1-pro-preview`,
`gemini-3-flash-preview`, `claude-opus-4-7`, and
`claude-sonnet-4-6`.
systematized_concepts_file: The file containing the systematized concepts.
It must be a `.yaml` file that contains a `systematized_concepts` key
with a list of systematized concepts. Each systematized concept must
Expand Down Expand Up @@ -87,7 +87,8 @@ async def evaluate(

logger.info("%s: Evaluating input text against all concepts...", datetime.datetime.now())

results = evaluate_text(text_to_evaluate, prompt_templates, model, api_key)
# Offload the synchronous OpenAI SDK call so the event loop stays responsive.
results = await asyncio.to_thread(evaluate_text, text_to_evaluate, prompt_templates, model, api_key)

evaluation_responses = []
for i in range(len(systematized_concepts)):
Expand Down
Loading