Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 10
Add score trace URL to evaluation run#558
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
87c8e4001ce874873631162d06730ac775e6e3d12eafc0d70788f3aea9a55ac5d9143387a64ffd662916941d02acc0ca1661c9082dccf4708c0fe52a43cdf0be7111dFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Add score_trace_url to evaluation_run | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Revision ID: 044 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Revises: 043 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Create Date: 2026-01-24 19:34:46.763908 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from alembic import op | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import sqlalchemy as sa | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import sqlmodel.sql.sqltypes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| revision = "044" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| down_revision = "043" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| branch_labels = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| depends_on = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def upgrade(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| op.add_column( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "evaluation_run", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sa.Column( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "score_trace_url", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sqlmodel.sql.sqltypes.AutoString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nullable=True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| comment="S3 URL where per-trace evaluation scores are stored", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def downgrade(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| op.drop_column("evaluation_run", "score_trace_url") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+19
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add return type hints to migration functions. Both ✍️ Suggested update-def upgrade():+def upgrade() -> None:
op.add_column(
"evaluation_run",
sa.Column(
"score_trace_url",
sqlmodel.sql.sqltypes.AutoString(),
nullable=True,
comment="S3 URL where per-trace evaluation scores are stored",
),
)
-def downgrade():+def downgrade() -> None:
op.drop_column("evaluation_run", "score_trace_url")As per coding guidelines, **/*.py: Always add type hints to all function parameters and return values in Python code. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -13,6 +13,10 @@ | ||
| from app.models.llm.request import ConfigBlob, LLMCallConfig | ||
| from app.services.llm.jobs import resolve_config_blob | ||
| from app.core.db import engine | ||
| from app.core.cloud.storage import get_cloud_storage | ||
| from app.core.storage_utils import upload_jsonl_to_object_store | ||
| logger = logging.getLogger(__name__) | ||
| @@ -187,6 +191,7 @@ def update_evaluation_run( | ||
| status: str | None = None, | ||
| error_message: str | None = None, | ||
| object_store_url: str | None = None, | ||
| score_trace_url: str | None = None, | ||
| score: dict | None = None, | ||
| embedding_batch_job_id: int | None = None, | ||
| ) -> EvaluationRun: | ||
| @@ -219,6 +224,8 @@ def update_evaluation_run( | ||
| eval_run.score = score | ||
| if embedding_batch_job_id is not None: | ||
| eval_run.embedding_batch_job_id = embedding_batch_job_id | ||
| if score_trace_url is not None: | ||
| eval_run.score_trace_url = score_trace_url or None | ||
| # Always update timestamp | ||
| eval_run.updated_at = now() | ||
| @@ -335,7 +342,6 @@ def save_score( | ||
| Returns: | ||
| Updated EvaluationRun instance, or None if not found | ||
| """ | ||
| from app.core.db import engine | ||
| with Session(engine) as session: | ||
| eval_run = get_evaluation_run_by_id( | ||
| @@ -344,12 +350,61 @@ def save_score( | ||
| organization_id=organization_id, | ||
| project_id=project_id, | ||
| ) | ||
| if eval_run: | ||
| update_evaluation_run(session=session, eval_run=eval_run, score=score) | ||
| logger.info( | ||
| f"[save_score] Saved score | evaluation_id={eval_run_id} | " | ||
| f"traces={len(score.get('traces', []))}" | ||
| ) | ||
| if not eval_run: | ||
| return None | ||
| traces = score.get("traces", []) | ||
| summary_score = score.get("summary_scores", []) | ||
| score_trace_url: str | None = "" if not traces else None | ||
| if traces: | ||
| try: | ||
| storage = get_cloud_storage(session=session, project_id=project_id) | ||
| score_trace_url = upload_jsonl_to_object_store( | ||
| storage=storage, | ||
| results=traces, | ||
| filename=f"traces_{eval_run_id}.json", | ||
| subdirectory=f"evaluations/score/{eval_run_id}", | ||
| format="json", | ||
| ) | ||
| if score_trace_url: | ||
| logger.info( | ||
| f"[save_score] uploaded traces to S3 | " | ||
| f"evaluation_id={eval_run_id} | url={score_trace_url} | " | ||
| f"traces_count={len(traces)}" | ||
| ) | ||
| else: | ||
| logger.warning( | ||
| f"[save_score] failed to upload traces to S3, " | ||
| f"falling back to DB storage | evaluation_id={eval_run_id}" | ||
| ) | ||
| except Exception as e: | ||
| logger.error( | ||
| f"[save_score] Error uploading traces to S3: {e} | " | ||
| f"evaluation_id={eval_run_id}", | ||
| exc_info=True, | ||
| ) | ||
| # IF TRACES DATA IS STORED IN S3 URL THEN HERE WE ARE JUST STORING THE SUMMARY SCORE | ||
| # TODO: Evaluate whether this behaviour is needed or completely discard the storing data in db | ||
| if score_trace_url: | ||
| db_score = {"summary_scores": summary_score} | ||
| else: | ||
| # fallback to store data in db if failed to store in s3 | ||
| db_score = score | ||
| update_evaluation_run( | ||
| session=session, | ||
| eval_run=eval_run, | ||
| score=db_score, | ||
| score_trace_url=score_trace_url, | ||
| ) | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| logger.info( | ||
| f"[save_score] Saved score | evaluation_id={eval_run_id} | " | ||
| f"traces={len(score.get('traces', []))}" | ||
| ) | ||
| return eval_run | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -18,6 +18,8 @@ | ||
| from app.models.evaluation import EvaluationRun | ||
| from app.services.llm.providers import LLMProvider | ||
| from app.utils import get_langfuse_client, get_openai_client | ||
| from app.core.cloud.storage import get_cloud_storage | ||
| from app.core.storage_utils import load_json_from_object_store | ||
| logger = logging.getLogger(__name__) | ||
| @@ -189,6 +191,7 @@ def get_evaluation_with_scores( | ||
| Returns: | ||
| Tuple of (EvaluationRun or None, error_message or None) | ||
| """ | ||
| logger.info( | ||
| f"[get_evaluation_with_scores] Fetching status for evaluation run | " | ||
| f"evaluation_id={evaluation_id} | " | ||
| @@ -227,9 +230,41 @@ def get_evaluation_with_scores( | ||
| return eval_run, None | ||
| # Check if we already have cached traces | ||
| has_cached_traces = eval_run.score is not None and "traces" in eval_run.score | ||
| if not resync_score and has_cached_traces: | ||
| return eval_run, None | ||
| has_cached_traces_s3 = eval_run.score_trace_url is not None | ||
| has_cached_traces_db = eval_run.score is not None and "traces" in eval_run.score | ||
| if not resync_score: | ||
| if has_cached_traces_s3: | ||
| try: | ||
| storage = get_cloud_storage(session=session, project_id=project_id) | ||
| traces = load_json_from_object_store( | ||
| storage=storage, url=eval_run.score_trace_url | ||
| ) | ||
| if traces is not None: | ||
| eval_run.score = { | ||
| "summary_scores": (eval_run.score or {}).get( | ||
| "summary_scores", [] | ||
| ), | ||
| "traces": traces, | ||
| } | ||
| logger.info( | ||
| f"[get_evaluation_with_scores] Loaded traces from S3 | " | ||
| f"evaluation_id={evaluation_id} | " | ||
| f"traces_count={len(traces)}" | ||
| ) | ||
| return eval_run, None | ||
| except Exception as e: | ||
| logger.error( | ||
| f"[get_evaluation_with_scores] Error loading traces from S3: {e} | " | ||
| f"evaluation_id={evaluation_id}", | ||
| exc_info=True, | ||
| ) | ||
| if has_cached_traces_db: | ||
| logger.info( | ||
| f"[get_evaluation_with_scores] Returning traces from DB | " | ||
| f"evaluation_id={evaluation_id}" | ||
| ) | ||
| return eval_run, None | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| langfuse = get_langfuse_client( | ||
| session=session, | ||
| @@ -289,4 +324,7 @@ def get_evaluation_with_scores( | ||
| score=score, | ||
| ) | ||
| if eval_run: | ||
| eval_run.score = score | ||
| return eval_run, None | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.