-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat: frontend refactor + DR #5225
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 35 commits
1584ae5
16e0759
7f8b9ee
c341013
97a351f
6d2fc58
872f30f
c9e120c
3e84fce
6637a74
d4631aa
717d49c
3f2028f
13e1b9b
7d847a3
e930301
f40da40
a0bac8a
626f5f7
34dacbb
452aef5
350a203
b752213
e00f2eb
a9f038f
530df9e
701b279
8b2f9aa
6eb7b86
4195fde
f100401
693428f
0679d43
fd80348
fc1f602
3298e64
044d3c4
5b0644f
a466369
6b1f0fe
f335c21
8ee9ccd
84000ee
b3181d0
d3f1c68
3ec000a
9ce78cf
ca3b0c1
46f43ab
e6b6309
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,102 @@ | ||
"""add research agent database tables and chat message research fields | ||
Revision ID: 5ae8240accb3 | ||
Revises: b558f51620b4 | ||
Create Date: 2025-08-06 14:29:24.691388 | ||
""" | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
from sqlalchemy.dialects import postgresql | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = "5ae8240accb3" | ||
down_revision = "b558f51620b4" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
# Add research_type and research_plan columns to chat_message table | ||
op.add_column( | ||
"chat_message", | ||
sa.Column("research_type", sa.String(), nullable=True), | ||
) | ||
op.add_column( | ||
"chat_message", | ||
sa.Column("research_plan", postgresql.JSONB(), nullable=True), | ||
) | ||
|
||
# Create research_agent_iteration table | ||
op.create_table( | ||
"research_agent_iteration", | ||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), | ||
sa.Column( | ||
"primary_question_id", | ||
sa.Integer(), | ||
sa.ForeignKey("chat_message.id", ondelete="CASCADE"), | ||
nullable=False, | ||
), | ||
sa.Column("iteration_nr", sa.Integer(), nullable=False), | ||
sa.Column( | ||
"created_at", | ||
sa.DateTime(timezone=True), | ||
server_default=sa.func.now(), | ||
nullable=False, | ||
), | ||
sa.Column("purpose", sa.String(), nullable=True), | ||
sa.Column("reasoning", sa.String(), nullable=True), | ||
sa.PrimaryKeyConstraint("id"), | ||
) | ||
|
||
# Create research_agent_iteration_sub_step table | ||
op.create_table( | ||
"research_agent_iteration_sub_step", | ||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), | ||
sa.Column( | ||
"primary_question_id", | ||
sa.Integer(), | ||
sa.ForeignKey("chat_message.id", ondelete="CASCADE"), | ||
nullable=False, | ||
), | ||
sa.Column( | ||
"parent_question_id", | ||
sa.Integer(), | ||
sa.ForeignKey("research_agent_iteration_sub_step.id", ondelete="CASCADE"), | ||
nullable=True, | ||
), | ||
sa.Column("iteration_nr", sa.Integer(), nullable=False), | ||
sa.Column("iteration_sub_step_nr", sa.Integer(), nullable=False), | ||
sa.Column( | ||
"created_at", | ||
sa.DateTime(timezone=True), | ||
server_default=sa.func.now(), | ||
nullable=False, | ||
), | ||
sa.Column("sub_step_instructions", sa.String(), nullable=True), | ||
sa.Column( | ||
"sub_step_tool_id", | ||
sa.Integer(), | ||
sa.ForeignKey("tool.id"), | ||
nullable=True, | ||
), | ||
sa.Column("reasoning", sa.String(), nullable=True), | ||
sa.Column("sub_answer", sa.String(), nullable=True), | ||
sa.Column("cited_doc_results", postgresql.JSONB(), nullable=True), | ||
sa.Column("claims", postgresql.JSONB(), nullable=True), | ||
sa.Column("generated_images", postgresql.JSONB(), nullable=True), | ||
sa.Column("additional_data", postgresql.JSONB(), nullable=True), | ||
sa.PrimaryKeyConstraint("id"), | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
# Drop tables in reverse order | ||
op.drop_table("research_agent_iteration_sub_step") | ||
op.drop_table("research_agent_iteration") | ||
|
||
# Remove columns from chat_message table | ||
op.drop_column("chat_message", "research_plan") | ||
op.drop_column("chat_message", "research_type") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
"""migrate_agent_sub_questions_to_research_iterations | ||
Revision ID: bd7c3bf8beba | ||
Revises: f8a9b2c3d4e5 | ||
Create Date: 2025-08-18 11:33:27.098287 | ||
""" | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = "bd7c3bf8beba" | ||
down_revision = "f8a9b2c3d4e5" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
# Get connection to execute raw SQL | ||
connection = op.get_bind() | ||
|
||
# First, insert data into research_agent_iteration table | ||
# This creates one iteration record per primary_question_id using the earliest time_created | ||
connection.execute( | ||
sa.text( | ||
""" | ||
INSERT INTO research_agent_iteration (primary_question_id, created_at, iteration_nr, purpose, reasoning) | ||
SELECT | ||
primary_question_id, | ||
MIN(time_created) as created_at, | ||
1 as iteration_nr, | ||
'Generating and researching subquestions' as purpose, | ||
'(No previous reasoning)' as reasoning | ||
FROM agent__sub_question | ||
JOIN chat_message on agent__sub_question.primary_question_id = chat_message.id | ||
WHERE primary_question_id IS NOT NULL | ||
AND chat_message.is_agentic = true | ||
GROUP BY primary_question_id | ||
ON CONFLICT DO NOTHING; | ||
""" | ||
) | ||
) | ||
|
||
# Then, insert data into research_agent_iteration_sub_step table | ||
# This migrates each sub-question as a sub-step | ||
connection.execute( | ||
sa.text( | ||
""" | ||
INSERT INTO research_agent_iteration_sub_step ( | ||
primary_question_id, | ||
iteration_nr, | ||
iteration_sub_step_nr, | ||
created_at, | ||
sub_step_instructions, | ||
sub_step_tool_id, | ||
sub_answer, | ||
cited_doc_results | ||
) | ||
SELECT | ||
primary_question_id, | ||
1 as iteration_nr, | ||
level_question_num as iteration_sub_step_nr, | ||
time_created as created_at, | ||
sub_question as sub_step_instructions, | ||
1 as sub_step_tool_id, | ||
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. Hard-coded sub_step_tool_id=1 may violate FK to tool.id on environments where no tool with id=1 exists; use NULL or a looked-up id to avoid migration failure. Prompt for AI agents
|
||
sub_answer, | ||
sub_question_doc_results as cited_doc_results | ||
FROM agent__sub_question | ||
JOIN chat_message on agent__sub_question.primary_question_id = chat_message.id | ||
WHERE chat_message.is_agentic = true | ||
AND primary_question_id IS NOT NULL | ||
ON CONFLICT DO NOTHING; | ||
""" | ||
) | ||
) | ||
|
||
# Update chat_message records: set legacy agentic type and answer purpose for existing agentic messages | ||
connection.execute( | ||
sa.text( | ||
""" | ||
UPDATE chat_message | ||
SET research_answer_purpose = 'ANSWER' | ||
WHERE is_agentic = true | ||
AND research_type IS NULL and | ||
message_type = 'ASSISTANT'; | ||
""" | ||
) | ||
) | ||
connection.execute( | ||
sa.text( | ||
""" | ||
UPDATE chat_message | ||
SET research_type = 'LEGACY_AGENTIC' | ||
WHERE is_agentic = true | ||
AND research_type IS NULL; | ||
""" | ||
) | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
# Get connection to execute raw SQL | ||
connection = op.get_bind() | ||
|
||
# Note: This downgrade removes all research agent iteration data | ||
# There's no way to perfectly restore the original agent__sub_question data | ||
# if it was deleted after this migration | ||
|
||
# Delete all research_agent_iteration_sub_step records that were migrated | ||
connection.execute( | ||
sa.text( | ||
""" | ||
DELETE FROM research_agent_iteration_sub_step | ||
USING chat_message | ||
WHERE research_agent_iteration_sub_step.primary_question_id = chat_message.id | ||
AND chat_message.research_type = 'LEGACY_AGENTIC'; | ||
""" | ||
) | ||
) | ||
|
||
# Delete all research_agent_iteration records that were migrated | ||
connection.execute( | ||
sa.text( | ||
""" | ||
DELETE FROM research_agent_iteration | ||
USING chat_message | ||
WHERE research_agent_iteration.primary_question_id = chat_message.id | ||
AND chat_message.research_type = 'LEGACY_AGENTIC'; | ||
""" | ||
) | ||
) | ||
|
||
# Revert chat_message updates: clear research fields for legacy agentic messages | ||
connection.execute( | ||
sa.text( | ||
""" | ||
UPDATE chat_message | ||
SET research_type = NULL, | ||
research_answer_purpose = NULL | ||
WHERE is_agentic = true | ||
AND research_type = 'LEGACY_AGENTIC' | ||
AND message_type = 'ASSISTANT'; | ||
""" | ||
) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
"""add research_answer_purpose to chat_message | ||
|
||
Revision ID: f8a9b2c3d4e5 | ||
Revises: 5ae8240accb3 | ||
Create Date: 2025-01-27 12:00:00.000000 | ||
|
||
""" | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = "f8a9b2c3d4e5" | ||
down_revision = "5ae8240accb3" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
# Add research_answer_purpose column to chat_message table | ||
op.add_column( | ||
"chat_message", | ||
sa.Column("research_answer_purpose", sa.String(), nullable=True), | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
# Remove research_answer_purpose column from chat_message table | ||
op.drop_column("chat_message", "research_answer_purpose") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,17 @@ | ||
from ee.onyx.server.query_and_chat.models import OneShotQAResponse | ||
from onyx.chat.models import AllCitations | ||
from onyx.chat.models import AnswerStream | ||
from onyx.chat.models import LLMRelevanceFilterResponse | ||
from onyx.chat.models import OnyxAnswerPiece | ||
from onyx.chat.models import QADocsResponse | ||
from onyx.chat.models import StreamingError | ||
from onyx.chat.process_message import ChatPacketStream | ||
from onyx.server.query_and_chat.models import ChatMessageDetail | ||
from onyx.utils.timing import log_function_time | ||
|
||
|
||
@log_function_time() | ||
def gather_stream_for_answer_api( | ||
packets: ChatPacketStream, | ||
packets: AnswerStream, | ||
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. Parameter changed to AnswerStream but the function still checks for legacy stream types (OnyxAnswerPiece, QADocsResponse, AllCitations, ChatMessageDetail), so packets will not match and the response will be incomplete or empty. Update processing to handle Packet/Message* and MessageResponseIDInfo to align with AnswerStream. Prompt for AI agents
|
||
) -> OneShotQAResponse: | ||
response = OneShotQAResponse() | ||
|
||
|
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.
Sub-steps are not linked to a specific iteration via a foreign key, allowing inconsistent data. Add an iteration_id FK (or a composite FK using a unique constraint) to enforce referential integrity.
Prompt for AI agents