Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5aad042
fix: merge conflict
Prajna1999 Jan 20, 2026
f47c20c
chore: update dependencies
Prajna1999 Jan 20, 2026
1e03961
feat: add google ai provider for Gemini models
Prajna1999 Jan 20, 2026
4ac4de8
feat: working stt with gemini and hotfixing circular import
Prajna1999 Jan 21, 2026
3c0bae7
Merge branch 'main' into feature/unified-api-stt-new
Prajna1999 Jan 21, 2026
7db94f1
Merge branch 'main' into feature/unified-api-stt-new
Prajna1999 Jan 21, 2026
196eb5c
feat: llm_call table, type enforce gAI stt response
Prajna1999 Jan 21, 2026
dca3139
Merge remote-tracking branch 'refs/remotes/origin/feature/unified-api…
Prajna1999 Jan 21, 2026
271d677
feat: discriminated union type enforcing for stt, tts and text comple…
Prajna1999 Jan 22, 2026
5ae59e5
Merge branch 'main' into feature/unified-api-stt-new
Prajna1999 Jan 22, 2026
250ce9f
fix: type annotation
Prajna1999 Jan 22, 2026
1742a8b
chore: fix alembic revision for shure
Prajna1999 Jan 23, 2026
ebb2394
feat: add google stt task to async job
Prajna1999 Jan 23, 2026
0bcb697
feat: yolo commit and linting issues
Prajna1999 Jan 26, 2026
a6850a3
feat: query input takes audio_url and base64 as audio file input
Prajna1999 Jan 26, 2026
f4693f6
chore: test cases for google ai and async job fixes, supress mappers …
Prajna1999 Jan 26, 2026
909e249
fix: test cases for config
Prajna1999 Jan 27, 2026
a7b0062
chore: clean PLAN.md
Prajna1999 Jan 27, 2026
fa25199
chore: extract stt code into its own
Prajna1999 Jan 28, 2026
24007a2
Merge branch 'main' into feature/unified-api-stt-new
Prajna1999 Jan 29, 2026
bbd2c7f
Refactor evaluation endpoint to use stored configuration and remove a…
avirajsingh7 Dec 9, 2025
b907440
fix: default original provider bug
Prajna1999 Jan 30, 2026
9f38f45
fix: coderrabbit comments
Prajna1999 Jan 31, 2026
f6348b5
Merge branch 'main' into feature/unified-api-stt-new
Prajna1999 Jan 31, 2026
b3ea8ec
fix: migration number
Prajna1999 Jan 31, 2026
26e0a6a
chore: formatting issue solved
Prajna1999 Jan 31, 2026
a623efa
fix: eval core crud test cases
Prajna1999 Jan 31, 2026
5c86cf2
fix: test cases for evaluation and test_llm
Prajna1999 Feb 1, 2026
c8f165a
chore: test formatting reset to main
Prajna1999 Feb 3, 2026
19a6ef7
chore: fix formatting issues
Prajna1999 Feb 3, 2026
9bf057b
chore: squash llm_call table migration to sno.43
Prajna1999 Feb 3, 2026
665102e
chore: change SQL model signature from ConfigVersionCreatePartial to…
Prajna1999 Feb 3, 2026
325ff4d
fix: remove extra imports and add util functions
Prajna1999 Feb 4, 2026
237dd97
fix: change llm_call input type and other changes
Prajna1999 Feb 5, 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
201 changes: 201 additions & 0 deletions backend/app/alembic/versions/043_add_llm_call_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
"""add_llm_call_table

Revision ID: 043
Revises: 042
Create Date: 2026-01-26 15:20:23.873332

"""
from alembic import op
import sqlalchemy as sa
import sqlmodel.sql.sqltypes
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "043"
down_revision = "042"
branch_labels = None
depends_on = None


def upgrade():
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find backend -name "043_add_llm_call_table.py" -type f

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 129


🏁 Script executed:

if [ -f "backend/app/alembic/versions/043_add_llm_call_table.py" ]; then
  wc -l "backend/app/alembic/versions/043_add_llm_call_table.py"
  echo "---"
  sed -n '15,25p' "backend/app/alembic/versions/043_add_llm_call_table.py"
  echo "---"
  sed -n '167,177p' "backend/app/alembic/versions/043_add_llm_call_table.py"
else
  echo "File not found"
fi

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 629


Add return type hints to upgrade and downgrade functions.

Line 20 and line 172 define migration functions without return types. Add -> None to both to comply with project guidelines requiring type hints on all function parameters and return values.

✏️ Suggested typing update
-def upgrade():
+def upgrade() -> None:
@@
-def downgrade():
+def downgrade() -> None:
🤖 Prompt for AI Agents
In `@backend/app/alembic/versions/043_add_llm_call_table.py` at line 20, The
migration functions upgrade and downgrade are missing return type hints; update
both function definitions (upgrade and downgrade) to include the return
annotation "-> None" (e.g., def upgrade() -> None: and def downgrade() -> None:)
so they comply with the project's typing guidelines while leaving the function
bodies unchanged.

# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"llm_call",
sa.Column(
"id",
sa.Uuid(),
nullable=False,
comment="Unique identifier for the LLM call record",
),
sa.Column(
"job_id",
sa.Uuid(),
nullable=False,
comment="Reference to the parent job (status tracked in job table)",
),
sa.Column(
"project_id",
sa.Integer(),
nullable=False,
comment="Reference to the project this LLM call belongs to",
),
sa.Column(
"organization_id",
sa.Integer(),
nullable=False,
comment="Reference to the organization this LLM call belongs to",
),
sa.Column(
"input",
sqlmodel.sql.sqltypes.AutoString(),
nullable=False,
comment="User input - text string, binary data, or file path for multimodal",
),
sa.Column(
"input_type",
sa.String(),
nullable=False,
comment="Input type: text, audio, image",
),
sa.Column(
"output_type",
sa.String(),
nullable=True,
comment="Expected output type: text, audio, image",
),
sa.Column(
"provider",
sa.String(),
nullable=False,
comment="AI provider: openai, google, anthropic",
),
sa.Column(
"model",
sqlmodel.sql.sqltypes.AutoString(),
nullable=False,
comment="Specific model used e.g. 'gpt-4o', 'gemini-2.5-pro'",
),
sa.Column(
"provider_response_id",
sqlmodel.sql.sqltypes.AutoString(),
nullable=True,
comment="Original response ID from the provider (e.g., OpenAI's response ID)",
),
sa.Column(
"content",
postgresql.JSONB(astext_type=sa.Text()),
nullable=True,
comment="Response content: {text: '...'}, {audio_bytes: '...'}, or {image: '...'}",
),
sa.Column(
"usage",
postgresql.JSONB(astext_type=sa.Text()),
nullable=True,
comment="Token usage: {input_tokens, output_tokens, reasoning_tokens}",
),
sa.Column(
"conversation_id",
sqlmodel.sql.sqltypes.AutoString(),
nullable=True,
comment="Identifier linking this response to its conversation thread",
),
sa.Column(
"auto_create",
sa.Boolean(),
nullable=True,
comment="Whether to auto-create conversation if conversation_id doesn't exist (OpenAI specific)",
),
sa.Column(
"config",
postgresql.JSONB(astext_type=sa.Text()),
nullable=True,
comment="Configuration: {config_id, config_version} for stored config OR {config_blob} for ad-hoc config",
),
sa.Column(
"created_at",
sa.DateTime(),
nullable=False,
comment="Timestamp when the LLM call was created",
),
sa.Column(
"updated_at",
sa.DateTime(),
nullable=False,
comment="Timestamp when the LLM call was last updated",
),
sa.Column(
"deleted_at",
sa.DateTime(),
nullable=True,
comment="Timestamp when the record was soft-deleted",
),
sa.ForeignKeyConstraint(["job_id"], ["job.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(
["organization_id"], ["organization.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"idx_llm_call_conversation_id",
"llm_call",
["conversation_id"],
unique=False,
postgresql_where=sa.text("conversation_id IS NOT NULL AND deleted_at IS NULL"),
)
op.create_index(
"idx_llm_call_job_id",
"llm_call",
["job_id"],
unique=False,
postgresql_where=sa.text("deleted_at IS NULL"),
)
op.alter_column(
"collection",
"llm_service_name",
existing_type=sa.VARCHAR(),
comment="Name of the LLM service",
existing_comment="Name of the LLM service provider",
existing_nullable=False,
)
Comment on lines +20 to +160
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the current state of the upgrade function in the migration file
head -30 backend/app/alembic/versions/042_add_llm_call_table.py

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 718


🏁 Script executed:

# Check other migration files to see if they have type hints
find backend/app/alembic/versions -name "*.py" -type f | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && head -25 {}'

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 3370


🏁 Script executed:

# Check if the file has a downgrade() function as well
grep -n "def downgrade" backend/app/alembic/versions/042_add_llm_call_table.py

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 95


🏁 Script executed:

# Check the downgrade function and surrounding context
sed -n '164,170p' backend/app/alembic/versions/042_add_llm_call_table.py

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 316


Add return type hint to upgrade function.

Line 20 defines upgrade without a return type; add -> None to meet the typing requirement.

✏️ Suggested fix
-def upgrade():
+def upgrade() -> None:

Per coding guidelines, all **/*.py files must include type hints for function parameters and return values.

🤖 Prompt for AI Agents
In `@backend/app/alembic/versions/042_add_llm_call_table.py` around lines 20 -
160, The function definition for upgrade is missing a return type; update the
def upgrade() signature to include an explicit return type (-> None) so it reads
def upgrade() -> None: and ensure the function body remains unchanged; apply the
same pattern for any Alembic migration functions (e.g., downgrade) in this file
if present to satisfy typing guidelines.

op.alter_column(
"llm_call",
"provider",
existing_type=sa.VARCHAR(),
comment="AI provider as sent by user (e.g openai, -native, google)",
existing_comment="AI provider: openai, google, anthropic",
existing_nullable=False,
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"collection",
"llm_service_name",
existing_type=sa.VARCHAR(),
comment="Name of the LLM service provider",
existing_comment="Name of the LLM service",
existing_nullable=False,
)
op.alter_column(
"llm_call",
"provider",
existing_type=sa.VARCHAR(),
comment="AI provider: openai, google, anthropic",
existing_comment="AI provider as sent by user (e.g openai, -native, google)",
existing_nullable=False,
)
op.drop_index(
"idx_llm_call_job_id",
table_name="llm_call",
postgresql_where=sa.text("deleted_at IS NULL"),
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update this migration with updated name instead

op.drop_index(
"idx_llm_call_conversation_id",
table_name="llm_call",
postgresql_where=sa.text("conversation_id IS NOT NULL AND deleted_at IS NULL"),
)
op.drop_table("llm_call")
# ### end Alembic commands ###
11 changes: 7 additions & 4 deletions backend/app/api/routes/config/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from app.api.deps import SessionDep, AuthContextDep
from app.crud.config import ConfigCrud, ConfigVersionCrud
from app.models import (
ConfigVersionCreate,
ConfigVersionUpdatePartial,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn;t get why use partial in the name

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all updates are partial update since type field is immutable.

ConfigVersionPublic,
Message,
ConfigVersionItems,
Expand All @@ -24,18 +24,21 @@
)
def create_version(
config_id: UUID,
version_create: ConfigVersionCreate,
version_create: ConfigVersionUpdatePartial,
current_user: AuthContextDep,
session: SessionDep,
):
Comment on lines 25 to 30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add an explicit return type annotation.

The endpoint is missing a return type, which is required by the Python typing guideline.

🔧 Suggested fix
 def create_version(
     config_id: UUID,
     version_create: ConfigVersionUpdatePartial,
     current_user: AuthContextDep,
     session: SessionDep,
-) :
+) -> APIResponse[ConfigVersionPublic]:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def create_version(
config_id: UUID,
version_create: ConfigVersionCreate,
version_create: ConfigVersionUpdatePartial,
current_user: AuthContextDep,
session: SessionDep,
):
def create_version(
config_id: UUID,
version_create: ConfigVersionUpdatePartial,
current_user: AuthContextDep,
session: SessionDep,
) -> APIResponse[ConfigVersionPublic]:
🤖 Prompt for AI Agents
In `@backend/app/api/routes/config/version.py` around lines 25 - 30, The
create_version endpoint is missing an explicit return type; update the function
signature for create_version (which takes ConfigVersionUpdatePartial,
AuthContextDep, SessionDep) to include the appropriate typed return annotation
(e.g., -> ConfigVersionRead or your project's response model) so the function
signature explicitly declares its return type per typing guidelines; ensure the
imported return model is used and adjust any related type imports if necessary.

"""
Create a new version for an existing configuration.
The version number is automatically incremented.

Only include the fields you want to update in config_blob.
Provider, model, and params can be changed.
Type is inherited from existing config and cannot be changed.
"""
version_crud = ConfigVersionCrud(
session=session, project_id=current_user.project_.id, config_id=config_id
)
version = version_crud.create_or_raise(version_create=version_create)
version = version_crud.create_from_partial_or_raise(version_create=version_create)

return APIResponse.success_response(
data=ConfigVersionPublic(**version.model_dump()),
Expand Down
40 changes: 37 additions & 3 deletions backend/app/core/langfuse/langfuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@
from asgi_correlation_id import correlation_id
from langfuse import Langfuse
from langfuse.client import StatefulGenerationClient, StatefulTraceClient
from app.models.llm import NativeCompletionConfig, QueryParams, LLMCallResponse
from app.models.llm import (
NativeCompletionConfig,
QueryParams,
LLMCallResponse,
TextOutput,
AudioOutput,
)

logger = logging.getLogger(__name__)


def extract_output_value(
llm_output: TextOutput | AudioOutput | None,
) -> str | dict[str, Any]:
"""Extract output value from LLM output for logging/tracing.

Args:
llm_output: The output (TextOutput, AudioOutput, or None)

Returns:
String value for text output, or dict with metadata for audio output
"""
if not llm_output:
return ""

if isinstance(llm_output, TextOutput):
return llm_output.content.value
elif isinstance(llm_output, AudioOutput):
# For audio, return metadata instead of the full base64 data
return {
"type": "audio",
"format": llm_output.content.format,
"mime_type": llm_output.content.mime_type,
"length": len(llm_output.content.value),
}
else:
return str(llm_output)


class LangfuseTracer:
def __init__(
self,
Expand Down Expand Up @@ -228,7 +262,7 @@ def langfuse_call(fn, *args, **kwargs):
generation.end,
output={
"status": "success",
"output": response.response.output.text,
"output": extract_output_value(response.response.output),
},
usage_details={
"input": response.usage.input_tokens,
Expand All @@ -241,7 +275,7 @@ def langfuse_call(fn, *args, **kwargs):
trace.update,
output={
"status": "success",
"output": response.response.output.text,
"output": extract_output_value(response.response.output),
},
session_id=session_id or response.response.conversation_id,
)
Expand Down
2 changes: 2 additions & 0 deletions backend/app/core/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Provider(str, Enum):
OPENAI = "openai"
AWS = "aws"
LANGFUSE = "langfuse"
GOOGLE = "google"


@dataclass
Expand All @@ -30,6 +31,7 @@ class ProviderConfig:
Provider.LANGFUSE: ProviderConfig(
required_fields=["secret_key", "public_key", "host"]
),
Provider.GOOGLE: ProviderConfig(required_fields=["api_key"]),
}


Expand Down
Loading