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
17 changes: 16 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,22 @@


## Testing Instructions
- Tests are currently still not working and should not be run

For comprehensive testing documentation, see [docs/designs/testing/README.md](docs/designs/testing/README.md).

Quick overview:
- **API Tests**: `cd api && python run-tests.py --api-url <api_url> --auth-key <auth_key> --license oss`
- **SDK Tests**: `cd sdk && python run-tests.py --api-url <api_url> --auth-key <auth_key> --license oss`
- **Web Tests**: `cd web/tests && AGENTA_WEB_URL=<web_url> TESTMAIL_NAMESPACE=<email_ns> TESTMAIL_API_KEY=<email_key> pnpm tsx playwright/scripts/run-tests.ts --coverage smoke`

Test documentation covers:
- Testing principles and philosophy
- Test boundaries (utils, unit, E2E)
- Test dimensions (coverage, path, case, lens, speed, license, cost, role, plan)
- Interface-specific guides (API, SDK, Web, Services)
- Test structure and organization
- Fixtures and utilities
- Running tests locally and in CI

## PR instructions
- If the user provides you with the issue id, title the PR: [issue-id] fix(frontend): <Title> where fix is the type (fix, feat, chore, ci, doc, test.. [we're using better-branch) and frontend is where and it could be API, SDK, frontend, docs, ..
Expand Down
2 changes: 1 addition & 1 deletion api/oss/src/apis/fastapi/auth/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ async def sso_callback_redirect(
if not is_ee():
raise HTTPException(
status_code=404,
detail="SSO/OIDC is only available in Enterprise Edition",
detail="SSO/OIDC is only available in EE",
)

try:
Expand Down
8 changes: 4 additions & 4 deletions api/oss/src/apis/fastapi/evaluations/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ async def edit_scenario(
):
raise FORBIDDEN_EXCEPTION # type: ignore

if str(scenario_id) != scenario_edit_request.scenario.id:
if str(scenario_id) != str(scenario_edit_request.scenario.id):
return EvaluationScenarioResponse()

scenario = await self.evaluations_service.edit_scenario(
Expand Down Expand Up @@ -1350,7 +1350,7 @@ async def edit_result(
):
raise FORBIDDEN_EXCEPTION # type: ignore

if str(result_id) != result_edit_request.result.id:
if str(result_id) != str(result_edit_request.result.id):
return EvaluationResultResponse()

result = await self.evaluations_service.edit_result(
Expand Down Expand Up @@ -1729,7 +1729,7 @@ async def edit_queue(
):
raise FORBIDDEN_EXCEPTION # type: ignore

if str(queue_id) != queue_edit_request.queue.id:
if str(queue_id) != str(queue_edit_request.queue.id):
return EvaluationQueueResponse()

queue = await self.evaluations_service.edit_queue(
Expand Down Expand Up @@ -2116,7 +2116,7 @@ async def edit_evaluation(
):
raise FORBIDDEN_EXCEPTION # type: ignore

if str(evaluation_id) != evaluation_edit_request.evaluation.id:
if str(evaluation_id) != str(evaluation_edit_request.evaluation.id):
return SimpleEvaluationResponse()

evaluation_edit = evaluation_edit_request.evaluation
Expand Down
2 changes: 1 addition & 1 deletion api/oss/src/apis/fastapi/workflows/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ async def commit_workflow_revision(
):
raise FORBIDDEN_EXCEPTION # type: ignore

if str(workflow_variant_id) != str(
if workflow_variant_id is not None and str(workflow_variant_id) != str(
workflow_revision_commit_request.workflow_revision.workflow_variant_id
):
return WorkflowRevisionResponse()
Expand Down
9 changes: 5 additions & 4 deletions api/oss/src/dbs/postgres/blobs/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,11 @@ async def query_blobs(
self.BlobDBE.tags.contains(blob_query.tags), # type: ignore
)

if blob_query.meta:
stmt = stmt.filter(
self.BlobDBE.meta.contains(blob_query.meta), # type: ignore
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if blob_query.meta:
# stmt = stmt.filter(
# self.BlobDBE.meta.contains(blob_query.meta),
# )

if windowing:
stmt = apply_windowing(
Expand Down
49 changes: 27 additions & 22 deletions api/oss/src/dbs/postgres/evaluations/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ async def close_run(
mode="json",
)

# run_dbe.flags["is_closed"] = True # type: ignore
run_dbe.flags["is_closed"] = True # type: ignore
flag_modified(run_dbe, "flags")

run_dbe.updated_at = datetime.now(timezone.utc) # type: ignore
Expand Down Expand Up @@ -537,7 +537,7 @@ async def close_runs(
mode="json",
)

# run_dbe.flags["is_closed"] = True # type: ignore
run_dbe.flags["is_closed"] = True # type: ignore
flag_modified(run_dbe, "flags")

run_dbe.updated_at = datetime.now(timezone.utc) # type: ignore
Expand Down Expand Up @@ -690,10 +690,11 @@ async def query_runs(
EvaluationRunDBE.tags.contains(run.tags),
)

if run.meta is not None:
stmt = stmt.filter(
EvaluationRunDBE.meta.contains(run.meta),
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if run.meta is not None:
# stmt = stmt.filter(
# EvaluationRunDBE.meta.contains(run.meta),
# )

if run.status is not None:
stmt = stmt.filter(
Expand Down Expand Up @@ -1245,10 +1246,11 @@ async def query_scenarios(
EvaluationScenarioDBE.tags.contains(scenario.tags),
)

if scenario.meta is not None:
stmt = stmt.filter(
EvaluationScenarioDBE.meta.contains(scenario.meta),
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if scenario.meta is not None:
# stmt = stmt.filter(
# EvaluationScenarioDBE.meta.contains(scenario.meta),
# )

if scenario.status is not None:
stmt = stmt.filter(
Expand Down Expand Up @@ -1765,10 +1767,11 @@ async def query_results(
EvaluationResultDBE.tags.contains(result.tags),
)

if result.meta is not None:
stmt = stmt.filter(
EvaluationResultDBE.meta.contains(result.meta),
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if result.meta is not None:
# stmt = stmt.filter(
# EvaluationResultDBE.meta.contains(result.meta),
# )

if result.status is not None:
stmt = stmt.filter(
Expand Down Expand Up @@ -2220,10 +2223,11 @@ async def query_metrics(
EvaluationMetricsDBE.tags.contains(metric.tags),
)

if metric.meta is not None:
stmt = stmt.filter(
EvaluationMetricsDBE.meta.contains(metric.meta),
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if metric.meta is not None:
# stmt = stmt.filter(
# EvaluationMetricsDBE.meta.contains(metric.meta),
# )

if metric.status is not None:
stmt = stmt.filter(
Expand Down Expand Up @@ -2679,10 +2683,11 @@ async def query_queues(
EvaluationQueueDBE.tags.contains(queue.tags),
)

if queue.meta is not None:
stmt = stmt.filter(
EvaluationQueueDBE.meta.contains(queue.meta),
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if queue.meta is not None:
# stmt = stmt.filter(
# EvaluationQueueDBE.meta.contains(queue.meta),
# )

if queue.name is not None:
stmt = stmt.filter(
Expand Down
5 changes: 3 additions & 2 deletions api/oss/src/dbs/postgres/folders/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,9 @@ async def query(
if folder_query.flags is not None:
stmt = stmt.filter(FolderDBE.flags.contains(folder_query.flags))

if folder_query.meta is not None:
stmt = stmt.filter(FolderDBE.meta.contains(folder_query.meta))
# meta is JSON (not JSONB) — containment (@>) is not supported
# if folder_query.meta is not None:
# stmt = stmt.filter(FolderDBE.meta.contains(folder_query.meta))

result = await session.execute(stmt)

Expand Down
42 changes: 26 additions & 16 deletions api/oss/src/dbs/postgres/git/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,10 +332,11 @@ async def query_artifacts(
self.ArtifactDBE.tags.contains(artifact_query.tags) # type: ignore
)

if artifact_query.meta:
stmt = stmt.filter(
self.ArtifactDBE.meta.contains(artifact_query.meta) # type: ignore
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if artifact_query.meta:
# stmt = stmt.filter(
# self.ArtifactDBE.meta.contains(artifact_query.meta)
# )

if artifact_query.name:
stmt = stmt.filter(
Expand Down Expand Up @@ -665,10 +666,11 @@ async def query_variants(
self.VariantDBE.tags.contains(variant_query.tags) # type: ignore
)

if variant_query.meta:
stmt = stmt.filter(
self.VariantDBE.meta.contains(variant_query.meta) # type: ignore
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if variant_query.meta:
# stmt = stmt.filter(
# self.VariantDBE.meta.contains(variant_query.meta)
# )

if variant_query.name:
stmt = stmt.filter(
Expand Down Expand Up @@ -877,7 +879,7 @@ async def create_revision(
revision.version = await self._get_version(
project_id=project_id,
variant_id=revision.variant_id, # type: ignore
created_at=revision.created_at, # type: ignore
revision_id=revision.id, # type: ignore
)

await self._set_version(
Expand Down Expand Up @@ -918,6 +920,13 @@ async def fetch_revision(
elif variant_ref:
if variant_ref.id:
stmt = stmt.filter(self.RevisionDBE.variant_id == variant_ref.id) # type: ignore
elif variant_ref.slug:
stmt = stmt.join(
self.VariantDBE,
self.RevisionDBE.variant_id == self.VariantDBE.id, # type: ignore
).filter(
self.VariantDBE.slug == variant_ref.slug, # type: ignore
)

if revision_ref and revision_ref.version:
stmt = stmt.filter(self.RevisionDBE.version == revision_ref.version) # type: ignore
Expand Down Expand Up @@ -1140,10 +1149,11 @@ async def query_revisions(
self.RevisionDBE.tags.contains(revision_query.tags) # type: ignore
)

if revision_query.meta:
stmt = stmt.filter(
self.RevisionDBE.meta.contains(revision_query.meta) # type: ignore
)
# meta is JSON (not JSONB) — containment (@>) is not supported
# if revision_query.meta:
# stmt = stmt.filter(
# self.RevisionDBE.meta.contains(revision_query.meta)
# )

if revision_query.author:
stmt = stmt.filter(
Expand Down Expand Up @@ -1271,7 +1281,7 @@ async def commit_revision(
revision.version = await self._get_version(
project_id=project_id,
variant_id=revision.variant_id, # type: ignore
created_at=revision.created_at, # type: ignore
revision_id=revision.id, # type: ignore
)

await self._set_version(
Expand Down Expand Up @@ -1394,7 +1404,7 @@ async def _get_version(
*,
project_id: UUID,
variant_id: UUID,
created_at: datetime,
revision_id: UUID,
) -> str:
async with engine.core_session() as session:
stmt = (
Expand All @@ -1403,7 +1413,7 @@ async def _get_version(
.where(
self.RevisionDBE.project_id == project_id, # type: ignore
self.RevisionDBE.variant_id == variant_id, # type: ignore
self.RevisionDBE.created_at < created_at, # type: ignore
self.RevisionDBE.id < revision_id, # type: ignore
)
)

Expand Down
9 changes: 5 additions & 4 deletions api/oss/src/services/variants_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,20 +993,21 @@ async def fork_config_by_variant_ref(
if app_variant_revision.data:
params = app_variant_revision.data.parameters or {}

# Build compound slug for the forked variant
# Build compound slug for the forked variant (always unique)
unique_suffix = uuid4().hex[-12:]
if variant_ref.slug:
# Fetch app to construct compound slug: {app_slug}.{variant_name}
# Fetch app to construct compound slug: {app_slug}.{variant_name}_{suffix}
app = await _fetch_app(
project_id=project_id,
app_id=app_variant.application_id,
)
if not app:
log.error(f"App not found for application_id: {app_variant.application_id}")
return None
fork_slug = f"{app.slug}.{variant_ref.slug}"
fork_slug = f"{app.slug}.{variant_ref.slug}_{unique_suffix}"
else:
# app_variant.slug is already compound; append a unique suffix
fork_slug = app_variant.slug + "_" + uuid4().hex[-12:]
fork_slug = app_variant.slug + "_" + unique_suffix

variant_slug, variant_version = await _create_variant(
project_id=project_id,
Expand Down
Loading