Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
1584ae5
squash: combine all DR commits into one
Weves Aug 20, 2025
16e0759
Fixes
Weves Aug 20, 2025
7f8b9ee
show KG in Assistant only if available
joachim-danswer Aug 20, 2025
c341013
KG only usable for KG Beta (for now)
joachim-danswer Aug 20, 2025
97a351f
base file upload
joachim-danswer Aug 20, 2025
6d2fc58
raise error if uploaded context is too long
joachim-danswer Aug 20, 2025
872f30f
improvements
Weves Aug 20, 2025
c9e120c
More improvements
Weves Aug 21, 2025
3e84fce
Fix citations
Weves Aug 21, 2025
6637a74
better decision making
joachim-danswer Aug 21, 2025
d4631aa
improved decision-making in Orchestrator
joachim-danswer Aug 21, 2025
717d49c
generic_internal tools
joachim-danswer Aug 22, 2025
3f2028f
Small tweak
Weves Aug 21, 2025
13e1b9b
tool use improvements
joachim-danswer Aug 23, 2025
7d847a3
add on
joachim-danswer Aug 23, 2025
e930301
More image gen stuff
Weves Aug 23, 2025
f40da40
fixes
Weves Aug 23, 2025
a0bac8a
Small color improvements
Weves Aug 23, 2025
626f5f7
Markdown utils
Weves Aug 23, 2025
34dacbb
fixed end conditions (incl early exit for image generation)
joachim-danswer Aug 24, 2025
452aef5
remove agent search + image fixes
joachim-danswer Aug 24, 2025
350a203
Okta tool support for reload
joachim-danswer Aug 24, 2025
b752213
Some cleanup
Weves Aug 24, 2025
e00f2eb
Stream back search tool results as they come
Weves Aug 25, 2025
a9f038f
tool forcing
joachim-danswer Aug 25, 2025
530df9e
fixed no-Tool-Assistant
joachim-danswer Aug 25, 2025
701b279
Support anthropic tool calling
Weves Aug 25, 2025
8b2f9aa
Support anthropic models better
Weves Aug 25, 2025
6eb7b86
More stuff
Weves Aug 25, 2025
4195fde
prompt fixes and search step numbers
joachim-danswer Aug 25, 2025
f100401
Fix hook ordering issue
Weves Aug 25, 2025
693428f
internal search fix
joachim-danswer Aug 25, 2025
0679d43
Improve citation look
Weves Aug 25, 2025
fd80348
Small UI improvements
Weves Aug 25, 2025
fc1f602
Improvements
Weves Aug 25, 2025
3298e64
Improve dot
Weves Aug 25, 2025
044d3c4
Small chat fixes
Weves Aug 26, 2025
5b0644f
Small UI tweaks
Weves Aug 26, 2025
a466369
Small improvements
Weves Aug 26, 2025
6b1f0fe
Remove un-used code
Weves Aug 26, 2025
f335c21
Fix
Weves Aug 26, 2025
8ee9ccd
Remove test_answer.py for now
Weves Aug 26, 2025
84000ee
Fix
Weves Aug 26, 2025
b3181d0
improvements
joachim-danswer Aug 26, 2025
d3f1c68
Add foreign keys
Weves Aug 26, 2025
3ec000a
Merge branch 'dr-cw' into dr-merge_v2
Weves Aug 26, 2025
9ce78cf
early forcing
joachim-danswer Aug 26, 2025
ca3b0c1
Fix tests
Weves Aug 26, 2025
46f43ab
Merge branch 'dr-cw-1' into dr-merge_v2
Weves Aug 26, 2025
e6b6309
Fix tests
Weves Aug 26, 2025
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
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),
Copy link
Contributor

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
Address the following comment on backend/alembic/versions/5ae8240accb3_add_research_agent_database_tables_and_.py at line 42:

<comment>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.</comment>

<file context>
@@ -0,0 +1,102 @@
+&quot;&quot;&quot;add research agent database tables and chat message research fields
+
+Revision ID: 5ae8240accb3
+Revises: b558f51620b4
+Create Date: 2025-08-06 14:29:24.691388
+
+&quot;&quot;&quot;
+
+from alembic import op
</file context>

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,
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Address the following comment on backend/alembic/versions/bd7c3bf8beba_migrate_agent_responses_to_research_.py at line 67:

<comment>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.</comment>

<file context>
@@ -0,0 +1,147 @@
+&quot;&quot;&quot;migrate_agent_sub_questions_to_research_iterations
+
+Revision ID: bd7c3bf8beba
+Revises: f8a9b2c3d4e5
+Create Date: 2025-08-18 11:33:27.098287
+
+&quot;&quot;&quot;
+
+from alembic import op
</file context>

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")
4 changes: 2 additions & 2 deletions backend/ee/onyx/chat/process_message.py
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,
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Address the following comment on backend/ee/onyx/chat/process_message.py at line 14:

<comment>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.</comment>

<file context>
@@ -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
</file context>

) -> OneShotQAResponse:
response = OneShotQAResponse()

Expand Down
Loading
Loading