-
Couldn't load subscription status.
- Fork 5
Add API to Ingest OpenAI Assistant and Migrate to Support Multiple Vector Store IDs #280
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
Changes from all commits
77e5f86
b9dc757
7235dc4
88b4cf0
2f35b4f
7cb0a9d
7e86924
a4072e1
309a059
77ac3f2
a0203f6
233ab82
6dd98d7
63c3b26
b1296a0
af6a40b
091f78d
07cc058
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,58 @@ | ||
| """Change vector_store_id to vector_store_ids in openai_assistant table | ||
| Revision ID: f2589428c1d0 | ||
| Revises: 3389c67fdcb4 | ||
| Create Date: 2025-07-10 11:18:21.223114 | ||
| """ | ||
| from alembic import op | ||
| import sqlalchemy as sa | ||
| from sqlalchemy.dialects import postgresql | ||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision = "f2589428c1d0" | ||
| down_revision = "3389c67fdcb4" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
|
|
||
| def upgrade(): | ||
| # ### commands auto generated by Alembic - please adjust! ### | ||
| op.add_column( | ||
| "openai_assistant", | ||
| sa.Column("vector_store_ids", postgresql.ARRAY(sa.String()), nullable=True), | ||
| ) | ||
|
|
||
| op.execute( | ||
| """ | ||
| UPDATE openai_assistant | ||
| SET vector_store_ids = ARRAY[vector_store_id] | ||
| WHERE vector_store_id IS NOT NULL | ||
| """ | ||
| ) | ||
|
|
||
| op.drop_column("openai_assistant", "vector_store_id") | ||
| # ### end Alembic commands ### | ||
|
|
||
|
|
||
| def downgrade(): | ||
| # Add back the single vector_store_id column as nullable for safe data migration | ||
| op.add_column( | ||
| "openai_assistant", | ||
| sa.Column( | ||
| "vector_store_id", | ||
| sa.VARCHAR(length=255), | ||
| autoincrement=False, | ||
| nullable=True, # Allow nulls temporarily for safe migration | ||
| ), | ||
| ) | ||
|
|
||
| op.execute( | ||
| """ | ||
| UPDATE openai_assistant | ||
| SET vector_store_id = vector_store_ids[1] | ||
| WHERE vector_store_ids IS NOT NULL AND array_length(vector_store_ids, 1) > 0 | ||
| """ | ||
| ) | ||
|
|
||
| op.drop_column("openai_assistant", "vector_store_ids") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| from typing import Annotated | ||
|
|
||
| from fastapi import APIRouter, Depends, Path | ||
| from sqlmodel import Session | ||
|
|
||
| from app.api.deps import get_db, get_current_user_org_project | ||
| from app.crud import ( | ||
| fetch_assistant_from_openai, | ||
| sync_assistant, | ||
| ) | ||
| from app.models import UserProjectOrg | ||
| from app.utils import APIResponse, get_openai_client | ||
|
|
||
| router = APIRouter(prefix="/assistant", tags=["Assistants"]) | ||
|
|
||
|
|
||
| @router.post( | ||
| "/{assistant_id}/ingest", | ||
| response_model=APIResponse, | ||
| status_code=201, | ||
| ) | ||
| def ingest_assistant_route( | ||
| assistant_id: Annotated[str, Path(description="The ID of the assistant to ingest")], | ||
| session: Session = Depends(get_db), | ||
| current_user: UserProjectOrg = Depends(get_current_user_org_project), | ||
| ): | ||
| """ | ||
| Ingest an assistant from OpenAI and store it in the platform. | ||
| """ | ||
|
|
||
| client = get_openai_client( | ||
| session, current_user.organization_id, current_user.project_id | ||
| ) | ||
|
|
||
| openai_assistant = fetch_assistant_from_openai(assistant_id, client) | ||
| assistant = sync_assistant( | ||
| session=session, | ||
| organization_id=current_user.organization_id, | ||
| project_id=current_user.project_id, | ||
| openai_assistant=openai_assistant, | ||
| ) | ||
|
|
||
| return APIResponse.success_response(assistant) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,11 +125,11 @@ def process_response( | |
| "input": [{"role": "user", "content": request.question}], | ||
| } | ||
|
|
||
| if assistant.vector_store_id: | ||
| if assistant.vector_store_ids: | ||
| params["tools"] = [ | ||
| { | ||
| "type": "file_search", | ||
| "vector_store_ids": [assistant.vector_store_id], | ||
| "vector_store_ids": assistant.vector_store_ids, | ||
|
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. just confirm did u test if it comes in the same format and the API works 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. @AkhileshNegi vector_store_id does not exist in db. 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. so in the im sarying that vector_store_ids key expects value to be list, do we get list here and the API is working |
||
| "max_num_results": assistant.max_num_results, | ||
| } | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import pytest | ||
| from fastapi.testclient import TestClient | ||
| from unittest.mock import patch | ||
| from app.tests.utils.openai import mock_openai_assistant | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def normal_user_api_key_header(): | ||
| return {"X-API-KEY": "ApiKey Px8y47B6roJHin1lWLkR88eiDrFdXSJRZmFQazzai8j9"} | ||
|
|
||
|
|
||
| @patch("app.api.routes.assistants.fetch_assistant_from_openai") | ||
| def test_ingest_assistant_success( | ||
| mock_fetch_assistant, | ||
| client: TestClient, | ||
| normal_user_api_key_header: str, | ||
| ): | ||
| """Test successful assistant ingestion from OpenAI.""" | ||
| mock_assistant = mock_openai_assistant() | ||
|
|
||
| mock_fetch_assistant.return_value = mock_assistant | ||
|
|
||
| response = client.post( | ||
| f"/api/v1/assistant/{mock_assistant.id}/ingest", | ||
| headers=normal_user_api_key_header, | ||
| ) | ||
|
|
||
| assert response.status_code == 201 | ||
| response_json = response.json() | ||
| assert response_json["success"] is True | ||
| assert response_json["data"]["assistant_id"] == mock_assistant.id |
Uh oh!
There was an error while loading. Please reload this page.