Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
8018005
feat: added call-imports metadata
MSami625 Aug 7, 2026
6d46894
feat: added import-calls user metadata
MSami625 Aug 7, 2026
3115b13
feat: implement actor stamping for call import evaluations and user i…
MSami625 Aug 7, 2026
681fe37
feat: enhance user insights with improved call import evaluations
MSami625 Aug 7, 2026
8786a43
chore: add config.docker.yml to .gitignore
MSami625 Aug 7, 2026
55b3bfd
feat: Cache evaluation PDF reports by content fingerprint and reuse S…
MSami625 Aug 7, 2026
7579e28
refactor: update tests for new principal handling
MSami625 Aug 10, 2026
b3f5609
test: add is_enabled method to fake S3 for PDF report branding logo test
MSami625 Aug 10, 2026
ebad2cb
feat: add actor stamping to call import evaluation deletion process
MSami625 Aug 10, 2026
e1281b4
feat: enhance call import evaluation components with inline metadata …
MSami625 Aug 10, 2026
8cf5ec7
fix: update token revocation logic to use correct Redis set parameters
MSami625 Aug 10, 2026
78a664c
feat: implement build_eval_chain_import_apply_async function for impr…
MSami625 Aug 10, 2026
e10fec4
chore: placeholder
MSami625 Aug 11, 2026
0ce2d6a
fix: downloading call import evaluation PDF reports and UI improvments
MSami625 Aug 11, 2026
7e09155
feat: integrate LLM usage tracking and reporting across various compo…
MSami625 Aug 11, 2026
03a7070
Merge branch 'main' into token-usage
MSami625 Aug 12, 2026
f0ebac9
feat: enhance LLM/STT usage tracking with additional context and metr…
MSami625 Aug 12, 2026
16ab94a
Merge branch 'token-usage' of https://github.com/EfficientAI-tech/eff…
MSami625 Aug 12, 2026
09b9793
fix: committed claims management in db
MSami625 Aug 13, 2026
5c51608
refactor: enhanced Redis handling and database transaction integrity
MSami625 Aug 13, 2026
4dd9fdc
refactor: usage commited claims
MSami625 Aug 13, 2026
cbe1c47
feat: usage pricing and integrate usage context across various routes
MSami625 Aug 14, 2026
7d59a5b
feat: add usage flush configuration and enterprise entitlement checks…
MSami625 Aug 14, 2026
cf9547d
refactor: remove Celery beat service and update usage flush configura…
MSami625 Aug 15, 2026
96b0038
Merge remote-tracking branch 'origin/main' into token-usage
MSami625 Aug 15, 2026
468f246
fix: correct indentation in call_imports.py and improve comments in p…
MSami625 Aug 15, 2026
5694c1f
fix(usage): aggregate eval usage per evaluation instead of per record…
MSami625 Aug 15, 2026
a48dc64
feat: add enabled models support for AI providers and new endpoint fo…
MSami625 Aug 15, 2026
cae3bf8
fix(tests): update monkeypatch paths to reflect correct structure
MSami625 Aug 15, 2026
5e4ea48
feat: introduce Celery Beat for platform tasks and update Docker conf…
MSami625 Aug 17, 2026
02e8c0a
fix(migrations): widen source column in model_pricing_rates to VARCHA…
MSami625 Aug 17, 2026
7576d5f
fix(pricing): add clear_rates_table_cache function and update migrati…
MSami625 Aug 17, 2026
83fc098
fix(usage): update recompute flag to false in pricing overrides and u…
MSami625 Aug 17, 2026
49871cb
fix(usage): implement _cost_fields_for_pending_deltas function to str…
MSami625 Aug 17, 2026
a33972d
feat(docs): add usage tracking feature documentation and update relat…
MSami625 Aug 18, 2026
5f5810f
fix(config): update security settings and operational access configur…
MSami625 Aug 20, 2026
cc6292a
fix(tests): enhance test_switch_org_revokes_previous_session_tokens
MSami625 Aug 20, 2026
4d73dc4
Merge origin/main into s-compliance
MSami625 Aug 24, 2026
d6852ab
feat(auth): enhance logout functionality to support access tokens and…
MSami625 Aug 24, 2026
9cf4915
feat(auth): implement revocable session management for user and platf…
MSami625 Aug 24, 2026
d144bb5
fix(auth): improve error handling in session revocation
MSami625 Aug 24, 2026
e8d69d1
feat(usage): enhance LLM usage context management with new tracking f…
MSami625 Aug 25, 2026
5fc705d
feat(flexprice billing): implement tracking for call import evaluatio…
MSami625 Aug 25, 2026
35b69a2
feat(flexprice): add support for flexprice usage metering in various …
MSami625 Aug 25, 2026
5038b0f
refactor(playground): streamline post-call processing by consolidatin…
MSami625 Aug 26, 2026
88d6c45
feat(tests): enhance audio handling in evaluator result tests by intr…
MSami625 Aug 26, 2026
1abaae1
fix(tests): update evaluator and result IDs in bridge evaluator tests…
MSami625 Aug 26, 2026
5347af3
refactor(playground): rename and enhance usage tracking in post-call …
MSami625 Aug 26, 2026
9b4a734
refactor(playground): improve error handling and retry logic in post-…
MSami625 Aug 26, 2026
9f81f8f
feat(flexprice): implement local mocking for Flexprice billing during…
MSami625 Aug 26, 2026
7c277b2
refactor(flexprice): remove unused billing event calls and streamline…
MSami625 Aug 26, 2026
8dc478c
feat(flexprice): add billing support for evaluator recording minutes …
MSami625 Aug 26, 2026
a82a5cc
feat(flexprice): implement repair features functionality to repoint l…
MSami625 Aug 26, 2026
8dc36db
feat(flexprice): enhance subscription management by adding auto-subsc…
MSami625 Aug 26, 2026
5333874
feat(flexprice): add new billing events for agent test setup and prom…
MSami625 Aug 26, 2026
f8916f1
feat(playground): enhance call data processing by merging UI surface …
MSami625 Aug 28, 2026
88fe59d
feat(api): add traces support to evaluator results and playground rou…
MSami625 Sep 2, 2026
64bff2e
feat: UI updates in Call Traces and some fixes
MSami625 Sep 2, 2026
4bf951a
feat(api): enhance Agent trace ingestion with pipecat otel integration.
MSami625 Sep 3, 2026
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
3 changes: 3 additions & 0 deletions .codebase-memory/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Auto-generated by codebase-memory-mcp
# Prevent merge conflicts on compressed artifact
graph.db.zst binary merge=ours
Binary file added .codebase-memory/graph.db.zst
Binary file not shown.
4 changes: 4 additions & 0 deletions .github/workflows/backend-tests-postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
REDIS_URL: redis://localhost:6379/0
CELERY_BROKER_URL: redis://localhost:6379/0
CELERY_RESULT_BACKEND: redis://localhost:6379/0
EFFICIENTAI_PYTEST: "1"
FLEXPRICE_ENABLED: "false"

steps:
- name: Checkout repository
Expand Down Expand Up @@ -83,6 +85,8 @@ jobs:
SHARD_DATABASE_URL_02: postgresql://postgres:postgres@localhost:5432/efficientai_data_02
SHARDING_INTEGRATION_TEST: "1"
REDIS_URL: redis://localhost:6379/0
EFFICIENTAI_PYTEST: "1"
FLEXPRICE_ENABLED: "false"

steps:
- name: Checkout repository
Expand Down
208 changes: 105 additions & 103 deletions app/api/v1/api.py
Original file line number Diff line number Diff line change
@@ -1,103 +1,105 @@
"""API v1 router aggregation."""

from fastapi import APIRouter
from app.api.v1.routes import (
auth,
audio,
evaluations,
results,
agents,
personas,
scenarios,
iam,
profile,
integrations,
data_sources,
voicebundles,
aiproviders,
model_config,
manual_evaluations,
test_agents,
conversation_evaluations,
voice_agent,
evaluators,
evaluator_suites,
metrics,
evaluator_results,
chat,
playground,
settings,
observability,
alerts,
cron_jobs,
voice_playground,
public_blind_test,
prompt_partials,
prompt_optimization,
telephony,
vobiz_telephony,
call_imports,
call_import_schemas,
call_import_tags,
call_import_evaluations,
judge_alignment,
metric_studio,
workspaces,
workspace_iam,
dashboard,
llm_gateway,
platform_admin,
org_usage,
usage_pricing,
)

api_router = APIRouter()

# Include all route routers
api_router.include_router(auth.router)
api_router.include_router(audio.router)
api_router.include_router(evaluations.router)
api_router.include_router(results.router)
api_router.include_router(agents.router)
api_router.include_router(personas.router)
api_router.include_router(scenarios.router)
api_router.include_router(iam.router)
api_router.include_router(profile.router)
api_router.include_router(integrations.router)
api_router.include_router(data_sources.router)
api_router.include_router(voicebundles.router)
api_router.include_router(aiproviders.router)
api_router.include_router(model_config.router)
api_router.include_router(manual_evaluations.router)
api_router.include_router(test_agents.router)
api_router.include_router(conversation_evaluations.router)
api_router.include_router(voice_agent.router)
api_router.include_router(evaluators.router)
api_router.include_router(evaluator_suites.router)
api_router.include_router(metrics.router)
api_router.include_router(evaluator_results.router)
api_router.include_router(chat.router)
api_router.include_router(playground.router)
api_router.include_router(settings.router)
api_router.include_router(observability.router)
api_router.include_router(alerts.router)
api_router.include_router(cron_jobs.router)
api_router.include_router(voice_playground.router)
api_router.include_router(public_blind_test.router)
api_router.include_router(prompt_partials.router)
api_router.include_router(prompt_optimization.router)
api_router.include_router(telephony.router)
api_router.include_router(vobiz_telephony.router)
api_router.include_router(call_imports.router)
api_router.include_router(call_import_schemas.router)
api_router.include_router(call_import_tags.router)
api_router.include_router(call_import_evaluations.router)
api_router.include_router(judge_alignment.router)
api_router.include_router(metric_studio.router)
api_router.include_router(workspaces.router)
api_router.include_router(workspace_iam.router)
api_router.include_router(dashboard.router)
api_router.include_router(llm_gateway.router)
api_router.include_router(platform_admin.router)
api_router.include_router(org_usage.router)
api_router.include_router(usage_pricing.router)
"""API v1 router aggregation."""

from fastapi import APIRouter
from app.api.v1.routes import (
auth,
audio,
evaluations,
results,
agents,
personas,
scenarios,
iam,
profile,
integrations,
data_sources,
voicebundles,
aiproviders,
model_config,
manual_evaluations,
test_agents,
conversation_evaluations,
voice_agent,
evaluators,
evaluator_suites,
metrics,
evaluator_results,
chat,
playground,
settings,
observability,
alerts,
cron_jobs,
voice_playground,
public_blind_test,
prompt_partials,
prompt_optimization,
telephony,
vobiz_telephony,
call_imports,
call_import_schemas,
call_import_tags,
call_import_evaluations,
judge_alignment,
metric_studio,
workspaces,
workspace_iam,
dashboard,
llm_gateway,
platform_admin,
org_usage,
usage_pricing,
synthetic_traces,
)

api_router = APIRouter()

# Include all route routers
api_router.include_router(auth.router)
api_router.include_router(audio.router)
api_router.include_router(evaluations.router)
api_router.include_router(results.router)
api_router.include_router(agents.router)
api_router.include_router(personas.router)
api_router.include_router(scenarios.router)
api_router.include_router(iam.router)
api_router.include_router(profile.router)
api_router.include_router(integrations.router)
api_router.include_router(data_sources.router)
api_router.include_router(voicebundles.router)
api_router.include_router(aiproviders.router)
api_router.include_router(model_config.router)
api_router.include_router(manual_evaluations.router)
api_router.include_router(test_agents.router)
api_router.include_router(conversation_evaluations.router)
api_router.include_router(voice_agent.router)
api_router.include_router(evaluators.router)
api_router.include_router(evaluator_suites.router)
api_router.include_router(metrics.router)
api_router.include_router(evaluator_results.router)
api_router.include_router(chat.router)
api_router.include_router(playground.router)
api_router.include_router(settings.router)
api_router.include_router(observability.router)
api_router.include_router(alerts.router)
api_router.include_router(cron_jobs.router)
api_router.include_router(voice_playground.router)
api_router.include_router(public_blind_test.router)
api_router.include_router(prompt_partials.router)
api_router.include_router(prompt_optimization.router)
api_router.include_router(telephony.router)
api_router.include_router(vobiz_telephony.router)
api_router.include_router(call_imports.router)
api_router.include_router(call_import_schemas.router)
api_router.include_router(call_import_tags.router)
api_router.include_router(call_import_evaluations.router)
api_router.include_router(judge_alignment.router)
api_router.include_router(metric_studio.router)
api_router.include_router(workspaces.router)
api_router.include_router(workspace_iam.router)
api_router.include_router(dashboard.router)
api_router.include_router(llm_gateway.router)
api_router.include_router(platform_admin.router)
api_router.include_router(org_usage.router)
api_router.include_router(usage_pricing.router)
api_router.include_router(synthetic_traces.router)
36 changes: 34 additions & 2 deletions app/api/v1/routes/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
Agents API Routes
Complete CRUD operations for test agents
"""
from fastapi import APIRouter, Depends, HTTPException, status, Query
from uuid import uuid4

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status, Query
from fastapi.responses import JSONResponse, Response
from sqlalchemy.orm import Session
from typing import List, Optional
from uuid import UUID
from uuid import UUID, uuid4
import random
from pydantic import BaseModel
from loguru import logger

from app.dependencies import get_db, get_organization_id, get_workspace_id, get_api_key
from app.services.billing.flexprice_service import record_agent_test_setup_generated
from app.models.database import (
Agent, ConversationEvaluation, TestAgentConversation, VoiceBundle,
AIProvider, Integration, IntegrationPlatform, CallMediumEnum,
Expand Down Expand Up @@ -254,6 +257,7 @@ def _scenario_draft_responses(scenarios) -> list[GeneratedScenarioDraftResponse]
@router.post("/generate-test-prompt", response_model=GenerateTestPromptResponse)
async def generate_test_prompt(
data: GenerateTestPromptRequest,
background_tasks: BackgroundTasks,
organization_id: UUID = Depends(get_organization_id),
workspace_id: UUID = Depends(get_workspace_id),
api_key: str = Depends(get_api_key),
Expand Down Expand Up @@ -297,6 +301,14 @@ async def generate_test_prompt(
llm_config=data.llm_config,
credential_id=data.credential_id,
)
background_tasks.add_task(
record_agent_test_setup_generated,
organization_id,
uuid4(),
workspace_id=workspace_id,
purpose="test_prompt",
model=result.model,
)
return GenerateTestPromptResponse(
sections=_test_prompt_section_responses(result.sections),
test_agent_prompt=result.test_agent_prompt,
Expand All @@ -313,6 +325,7 @@ async def generate_test_prompt(
@router.post("/generate-scenarios-from-prompt", response_model=GenerateScenariosFromPromptResponse)
async def generate_scenarios_from_prompt(
data: GenerateScenariosFromPromptRequest,
background_tasks: BackgroundTasks,
organization_id: UUID = Depends(get_organization_id),
workspace_id: UUID = Depends(get_workspace_id),
api_key: str = Depends(get_api_key),
Expand Down Expand Up @@ -357,6 +370,15 @@ async def generate_scenarios_from_prompt(
llm_config=data.llm_config,
credential_id=data.credential_id,
)
background_tasks.add_task(
record_agent_test_setup_generated,
organization_id,
uuid4(),
workspace_id=workspace_id,
purpose="scenarios",
model=result.model,
scenario_count=len(result.scenarios),
)
return GenerateScenariosFromPromptResponse(
scenarios=_scenario_draft_responses(result.scenarios),
provider=result.provider,
Expand All @@ -372,6 +394,7 @@ async def generate_scenarios_from_prompt(
@router.post("/generate-test-setup", response_model=GenerateTestSetupResponse)
async def generate_test_setup(
data: GenerateTestSetupRequest,
background_tasks: BackgroundTasks,
organization_id: UUID = Depends(get_organization_id),
workspace_id: UUID = Depends(get_workspace_id),
api_key: str = Depends(get_api_key),
Expand Down Expand Up @@ -430,6 +453,15 @@ async def generate_test_setup(
llm_config=data.llm_config,
credential_id=data.credential_id,
)
background_tasks.add_task(
record_agent_test_setup_generated,
organization_id,
uuid4(),
workspace_id=workspace_id,
purpose="full_setup",
model=scenario_result.model,
scenario_count=len(scenario_result.scenarios),
)
return GenerateTestSetupResponse(
sections=_test_prompt_section_responses(prompt_result.sections),
test_agent_prompt=prompt_result.test_agent_prompt,
Expand Down
31 changes: 22 additions & 9 deletions app/api/v1/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ def _extract_bearer(authorization: Optional[str]) -> Optional[str]:
return token.strip()


def _revoke_local_password_access_token(bearer: str) -> None:
try:
claims = decode_access_token(bearer)
jti = claims.get("jti")
exp = claims.get("exp")
if jti and exp:
ttl = max(int(exp) - int(datetime.now(timezone.utc).timestamp()), 1)
revoke_access_jti(jti, ttl)
except JWTError:
pass


def _issue_session_tokens(
db: Session,
*,
Expand Down Expand Up @@ -611,15 +623,7 @@ def logout(
"""Revoke the current session's refresh token and blacklist the access token."""
bearer = _extract_bearer(authorization)
if bearer and principal.auth_method == AuthMethod.LOCAL_PASSWORD:
try:
claims = decode_access_token(bearer)
jti = claims.get("jti")
exp = claims.get("exp")
if jti and exp:
ttl = max(int(exp) - int(datetime.now(timezone.utc).timestamp()), 1)
revoke_access_jti(jti, ttl)
except JWTError:
pass
_revoke_local_password_access_token(bearer)

if payload and payload.refresh_token:
revoke_refresh_token(db, payload.refresh_token)
Expand Down Expand Up @@ -697,12 +701,14 @@ def refresh_session(payload: RefreshRequest, db: Session = Depends(get_db)) -> T

class SwitchOrgRequest(BaseModel):
organization_id: str
refresh_token: Optional[str] = None


@router.post("/switch-org", response_model=TokenResponse)
def switch_organization(
payload: SwitchOrgRequest,
principal: Principal = Depends(get_principal),
authorization: Optional[str] = Header(None, alias="Authorization"),
db: Session = Depends(get_db),
) -> TokenResponse:
"""
Expand Down Expand Up @@ -766,6 +772,13 @@ def switch_organization(
detail="User is no longer active.",
)

if principal.auth_method == AuthMethod.LOCAL_PASSWORD:
bearer = _extract_bearer(authorization)
if bearer:
_revoke_local_password_access_token(bearer)
if payload.refresh_token:
revoke_refresh_token(db, payload.refresh_token)

user.last_login_at = datetime.now(timezone.utc)
db.commit()
db.refresh(user)
Expand Down
Loading