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
109 changes: 109 additions & 0 deletions .github/workflows/pr-label-semver-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: PR Label Semantic Release

on:
pull_request_target:
types: [closed]
branches:
- main
- master

permissions:
contents: write

jobs:
release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- name: Determine bump type from PR labels
id: bump
uses: actions/github-script@v7
with:
script: |
const labels = (context.payload.pull_request.labels || []).map((label) =>
label.name.toLowerCase()
);
core.info(`PR labels: ${labels.join(", ") || "none"}`);

let bump = "patch";
if (labels.includes("major")) {
bump = "major";
} else if (labels.includes("minor")) {
bump = "minor";
} else if (labels.includes("fix") || labels.includes("patch")) {
bump = "patch";
} else {
core.info("No semver label found. Defaulting to patch bump.");
}

core.setOutput("bump", bump);

- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Compute next semantic version
id: version
shell: bash
run: |
set -euo pipefail

git fetch --tags --force
latest_tag="$(git tag -l 'v*' --sort=-v:refname | head -n 1)"

if [[ -z "$latest_tag" ]]; then
latest_tag="v0.0.0"
fi

current="${latest_tag#v}"
IFS='.' read -r major minor patch <<< "$current"

if [[ -z "${major:-}" || -z "${minor:-}" || -z "${patch:-}" ]]; then
echo "Latest tag '$latest_tag' is not semver format (v<major>.<minor>.<patch>)."
exit 1
fi

case "${{ steps.bump.outputs.bump }}" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
patch)
patch=$((patch + 1))
;;
*)
echo "Unknown bump type: ${{ steps.bump.outputs.bump }}"
exit 1
;;
esac

next_tag="v${major}.${minor}.${patch}"
echo "latest_tag=$latest_tag" >> "$GITHUB_OUTPUT"
echo "next_tag=$next_tag" >> "$GITHUB_OUTPUT"
echo "Next tag: $next_tag"

- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEXT_TAG: ${{ steps.version.outputs.next_tag }}
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
shell: bash
run: |
set -euo pipefail

if gh release view "$NEXT_TAG" >/dev/null 2>&1; then
echo "Release $NEXT_TAG already exists. Skipping."
exit 0
fi

gh release create "$NEXT_TAG" \
--target "$MERGE_SHA" \
--title "$NEXT_TAG" \
--generate-notes
2 changes: 1 addition & 1 deletion app/api/v1/routes/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async def generate_agent_description(
db: Session = Depends(get_db),
):
"""Generate an agent description using AI from a brief description."""
from app.services.llm_service import llm_service
from app.services.ai.llm_service import llm_service

if not data.description.strip():
raise HTTPException(400, "Description is required")
Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/routes/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
AlertHistoryResponse,
AlertHistoryUpdate,
)
from app.services.alert_evaluation_service import alert_evaluation_service
from app.services.alert_notification_service import alert_notification_service
from app.services.alerts.alert_evaluation_service import alert_evaluation_service
from app.services.alerts.alert_notification_service import alert_notification_service

router = APIRouter(prefix="/alerts", tags=["alerts"])

Expand Down
6 changes: 3 additions & 3 deletions app/api/v1/routes/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
from app.dependencies import get_api_key, get_organization_id
from app.models.database import AudioFile
from app.models.schemas import AudioFileResponse, MessageResponse
from app.services.storage_service import storage_service
from app.services.audio_service import AudioService
from app.services.s3_service import s3_service
from app.services.storage.storage_service import storage_service
from app.services.audio.audio_service import AudioService
from app.services.storage.s3_service import s3_service
from app.core.exceptions import AudioFileNotFoundError, StorageError
from uuid import UUID
from loguru import logger
Expand Down
2 changes: 1 addition & 1 deletion app/api/v1/routes/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pydantic import BaseModel

from app.dependencies import get_db, get_organization_id
from app.services.llm_service import llm_service
from app.services.ai.llm_service import llm_service
from app.models.schemas import ModelProvider

router = APIRouter(prefix="/chat", tags=["chat"])
Expand Down
6 changes: 3 additions & 3 deletions app/api/v1/routes/conversation_evaluations.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from app.dependencies import get_api_key, get_organization_id
from app.models.database import ConversationEvaluation, ManualTranscription, Agent, ModelProvider, Metric
from app.models.schemas import ConversationEvaluationCreate, ConversationEvaluationResponse, MessageResponse
from app.services.llm_service import llm_service
from app.services.voice_quality_service import is_audio_metric, calculate_audio_metrics, AUDIO_METRICS
from app.services.s3_service import s3_service
from app.services.ai.llm_service import llm_service
from app.services.audio.voice_quality_service import is_audio_metric, calculate_audio_metrics, AUDIO_METRICS
from app.services.storage.s3_service import s3_service

router = APIRouter(prefix="/conversation-evaluations", tags=["Conversation Evaluations"])

Expand Down
2 changes: 1 addition & 1 deletion app/api/v1/routes/data_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from app.dependencies import get_api_key, get_organization_id
from app.models.schemas import MessageResponse, S3ListFilesResponse, S3FileInfo, S3BrowseResponse, S3FolderInfo
from app.services.s3_service import s3_service
from app.services.storage.s3_service import s3_service
from app.core.exceptions import StorageError
from uuid import UUID

Expand Down
2 changes: 1 addition & 1 deletion app/api/v1/routes/evaluator_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def re_evaluate_result(
import uuid as _uuid
from app.models.database import Agent, Integration
from app.core.encryption import decrypt_api_key
from app.services.s3_service import s3_service
from app.services.storage.s3_service import s3_service

call_data = result.call_data or {}
platform = (result.provider_platform or "").lower()
Expand Down
2 changes: 1 addition & 1 deletion app/api/v1/routes/evaluators.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def format_custom_prompt(
db: Session = Depends(get_db),
):
"""Reformat a raw custom prompt into well-structured markdown using the org's LLM."""
from app.services.llm_service import llm_service
from app.services.ai.llm_service import llm_service
from app.models.database import AIProvider
from app.models.enums import ModelProvider

Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/routes/manual_evaluations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from app.dependencies import get_api_key, get_organization_id
from app.models.database import ManualTranscription, ModelProvider, AudioFile
from app.models.schemas import MessageResponse, S3ListFilesResponse, S3FileInfo
from app.services.transcription_service import transcription_service
from app.services.s3_service import s3_service
from app.services.ai.transcription_service import transcription_service
from app.services.storage.s3_service import s3_service

router = APIRouter(prefix="/manual-evaluations", tags=["Manual Evaluations"])

Expand Down
2 changes: 1 addition & 1 deletion app/api/v1/routes/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Dict, List, Any
from app.dependencies import get_api_key
from app.models.database import ModelProvider
from app.services.model_config_service import model_config_service
from app.services.ai.model_config_service import model_config_service

router = APIRouter(prefix="/model-config", tags=["Model Config"])

Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/routes/playground.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def poll_call_metrics(
try:
import requests as _http
import uuid as _uuid
from app.services.s3_service import s3_service
from app.services.storage.s3_service import s3_service

recording_urls = call_metrics.get("recording_urls", {})
audio_bytes = None
Expand Down Expand Up @@ -865,7 +865,7 @@ async def re_evaluate_call_recording(
"""
import requests as http_requests
import uuid as _uuid
from app.services.s3_service import s3_service
from app.services.storage.s3_service import s3_service

call_recording = db.query(CallRecording).filter(
CallRecording.call_short_id == call_short_id,
Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/routes/prompt_partials.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async def generate_prompt_with_ai(
db: Session = Depends(get_db),
):
"""Generate a new prompt using AI from a description."""
from app.services.llm_service import llm_service
from app.services.ai.llm_service import llm_service

if not data.description.strip():
raise HTTPException(400, "Description is required")
Expand Down Expand Up @@ -158,7 +158,7 @@ async def improve_prompt_with_ai(
db: Session = Depends(get_db),
):
"""Improve/reformat existing prompt content using AI."""
from app.services.llm_service import llm_service
from app.services.ai.llm_service import llm_service

if not data.content.strip():
raise HTTPException(400, "Content is required")
Expand Down
Loading
Loading