-
Notifications
You must be signed in to change notification settings - Fork 10
llm call: polling llm db endpoint #726
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
base: main
Are you sure you want to change the base?
Changes from 8 commits
edeb059
2861753
cb51ef7
20f96cb
05e9f57
5372913
b341f83
572e422
7c17313
82c1d81
8936ea5
a7e5b81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| Retrieve the status and results of an LLM call job by job ID. | ||
|
|
||
| This endpoint allows you to poll for the status and results of an asynchronous LLM call job that was previously initiated via the POST `/llm/call` endpoint. | ||
|
|
||
|
|
||
| ### Notes | ||
|
|
||
| - This endpoint returns both the job status AND the actual LLM response when complete | ||
| - LLM responses are also delivered asynchronously via the callback URL (if provided) | ||
| - Jobs can be queried at any time after creation |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,20 @@ | ||
| import logging | ||
| from uuid import UUID | ||
|
|
||
| from fastapi import APIRouter, Depends | ||
| from fastapi import APIRouter, Depends, HTTPException | ||
|
|
||
| from app.api.deps import AuthContextDep, SessionDep | ||
| from app.api.permissions import Permission, require_permission | ||
| from app.models import LLMCallRequest, LLMCallResponse, Message | ||
| from app.crud.jobs import JobCrud | ||
| from app.crud.llm import get_llm_calls_by_job_id | ||
| from app.models import ( | ||
| LLMCallRequest, | ||
| LLMCallResponse, | ||
| LLMJobImmediatePublic, | ||
| LLMJobPublic, | ||
| JobStatus, | ||
| ) | ||
| from app.models.llm.response import LLMResponse, Usage | ||
| from app.services.llm.jobs import start_job | ||
| from app.utils import APIResponse, validate_callback_url, load_description | ||
|
|
||
|
|
@@ -34,7 +44,7 @@ def llm_callback_notification(body: APIResponse[LLMCallResponse]): | |
| @router.post( | ||
| "/llm/call", | ||
| description=load_description("llm/llm_call.md"), | ||
| response_model=APIResponse[Message], | ||
| response_model=APIResponse[LLMJobImmediatePublic], | ||
| callbacks=llm_callback_router.routes, | ||
| dependencies=[Depends(require_permission(Permission.REQUIRE_PROJECT))], | ||
| ) | ||
|
|
@@ -43,22 +53,101 @@ def llm_call( | |
| ): | ||
| """ | ||
| Endpoint to initiate an LLM call as a background job. | ||
| Returns job information for polling. | ||
| """ | ||
| project_id = _current_user.project_.id | ||
| organization_id = _current_user.organization_.id | ||
|
|
||
| if request.callback_url: | ||
| validate_callback_url(str(request.callback_url)) | ||
|
|
||
| start_job( | ||
| job_id = start_job( | ||
| db=session, | ||
| request=request, | ||
| project_id=project_id, | ||
| organization_id=organization_id, | ||
| ) | ||
|
|
||
| return APIResponse.success_response( | ||
| data=Message( | ||
| message=f"Your response is being generated and will be delivered via callback." | ||
| ), | ||
| # Fetch job details to return immediate response | ||
| job_crud = JobCrud(session=session) | ||
|
Collaborator
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. jobcrud.create() should return the job_id no? I think |
||
| job = job_crud.get(job_id=job_id) | ||
|
|
||
| if not job: | ||
|
Collaborator
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. Like this line is not required if above is fixed |
||
| raise HTTPException(status_code=404, detail="Job not found") | ||
|
|
||
| if request.callback_url: | ||
| message = "Your response is being generated and will be delivered via callback." | ||
|
Collaborator
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. make the default global |
||
| else: | ||
| message = "Your response is being generated" | ||
|
|
||
| job_response = LLMJobImmediatePublic( | ||
|
Collaborator
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. the wrapper model is not required |
||
| job_id=job.id, | ||
| status=job.status.value, | ||
| message=message, | ||
| job_inserted_at=job.created_at, | ||
| job_updated_at=job.updated_at, | ||
| ) | ||
|
|
||
| return APIResponse.success_response(data=job_response) | ||
|
|
||
|
|
||
| @router.get( | ||
| "/llm/call/{job_id}", | ||
| description=load_description("llm/get_llm_call.md"), | ||
| response_model=APIResponse[LLMJobPublic], | ||
| dependencies=[Depends(require_permission(Permission.REQUIRE_PROJECT))], | ||
| ) | ||
| def get_llm_call_status( | ||
| _current_user: AuthContextDep, | ||
| session: SessionDep, | ||
| job_id: UUID, | ||
| ) -> APIResponse[LLMJobPublic]: | ||
| """ | ||
| Poll for LLM call job status and results. | ||
| Returns job information with nested LLM response when complete. | ||
| """ | ||
| job_crud = JobCrud(session=session) | ||
| job = job_crud.get(job_id=job_id) | ||
|
|
||
| if not job: | ||
| raise HTTPException(status_code=404, detail="Job not found") | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| llm_call_response = None | ||
| if job.status.value == JobStatus.SUCCESS: | ||
| llm_calls = get_llm_calls_by_job_id( | ||
| session=session, job_id=job_id, project_id=_current_user.project_.id | ||
| ) | ||
|
|
||
| if llm_calls: | ||
| # Get the first LLM call from the list which will be the only call for the job id | ||
| # since we initially won't be using this endpoint for llm chains | ||
| llm_call = llm_calls[0] | ||
|
|
||
| llm_response = LLMResponse( | ||
| provider_response_id=llm_call.provider_response_id or "", | ||
| conversation_id=llm_call.conversation_id, | ||
| provider=llm_call.provider, | ||
| model=llm_call.model, | ||
| output=llm_call.content, | ||
| ) | ||
|
|
||
| if not llm_call.usage: | ||
| raise HTTPException( | ||
|
Collaborator
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. It's okay if usage data is missing, log a warning instead of 500 |
||
| status_code=500, | ||
| detail="Completed LLM job is missing usage data", | ||
| ) | ||
|
|
||
| llm_call_response = LLMCallResponse( | ||
| response=llm_response, | ||
| usage=Usage(**llm_call.usage), | ||
| provider_raw_response=None, | ||
| ) | ||
|
|
||
| job_response = LLMJobPublic( | ||
| job_id=job.id, | ||
| status=job.status.value, | ||
| llm_response=llm_call_response, | ||
| error_message=job.error_message, | ||
| ) | ||
|
|
||
| return APIResponse.success_response(data=job_response) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,4 +30,7 @@ | |
| AudioOutput, | ||
| LLMChainResponse, | ||
| IntermediateChainResponse, | ||
| LLMJobBasePublic, | ||
|
Collaborator
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. I think these models are not required. There are total 7 fields russian doll-ed amongst the three. Use the strings instead |
||
| LLMJobImmediatePublic, | ||
| LLMJobPublic, | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we tried running this endpoint manually triggering with Postman or Swagger?