Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@shuyangli
Copy link
Member

@shuyangli shuyangli commented Jan 27, 2026

Sorry about the large PR!

This implements the main set of inference queries: list/get inferences, insert inferences, count inferences, and so on.

There's some duplication across InferenceQueries and InferenceCountQueries - will remove the InferenceCountQueries later. (Those were directly ported from the frontend.)

There's a small risk of SQL injection with metric names in joins, because of sqlx QueryBuilder interface limitations. We should consider revisiting it but because metric names are controlled by the clients, this is not a huge risk in practice (malicious users can't provide arbitrary metric names).

Step towards #5691.


Note

Medium Risk
Medium risk due to adding a new Postgres query/insert path for inferences and wiring it into live inference and batch write flows (with new pagination/order/filter logic and a schema migration). Potential correctness/performance issues or edge cases in query building (notably metric ORDER BY joins) could affect inference listing/counting and background writes.

Overview
Implements InferenceQueries for Postgres, including listing/counting inferences across chat_inferences and json_inferences, fetching function info/output/tool params/output schema, and batch inserting inference rows.

Refactors pagination validation into ListInferencesParams::validate_pagination() and updates ClickHouse queries to use it, while adding a Postgres-specific filter/ORDER BY builder (including metric-based ordering via JOINs) and a migration to convert chat_inferences.tool_choice to JSONB.

Wires inference writes in /inference and batch inference completion through DelegatingDatabaseConnection to always write to ClickHouse and optionally dual-write to Postgres behind feature flags, and adds/updates e2e tests plus new sqlx query metadata.

Written by Cursor Bugbot for commit 033b112. This will update automatically on new commits. Configure here.

@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch 4 times, most recently from f098854 to f98ca8b Compare January 27, 2026 22:35
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch from 7c0807d to a662ea1 Compare January 27, 2026 23:24
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from f98ca8b to 73bf4fa Compare January 27, 2026 23:24
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch from a662ea1 to 3d54063 Compare January 28, 2026 02:06
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from 73bf4fa to b30f8b4 Compare January 28, 2026 02:06
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch from 3d54063 to cdfd5a9 Compare January 28, 2026 03:15
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch 5 times, most recently from fd9a7b3 to a1c96c6 Compare January 28, 2026 14:51
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from b30f8b4 to e5d90b3 Compare January 28, 2026 15:01
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch 3 times, most recently from 64cacd9 to 6e9cc21 Compare January 28, 2026 17:24
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch from a1c96c6 to 9e2cdae Compare January 28, 2026 18:38
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch 2 times, most recently from 5bcdb93 to cb64a5e Compare January 28, 2026 18:40
@shuyangli shuyangli force-pushed the sl/postgres-feedback-tables-and-writes branch 2 times, most recently from 4c85997 to b8838e8 Compare January 28, 2026 19:15
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from cb64a5e to ac7926c Compare January 28, 2026 19:17
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from ac7926c to 43cfaef Compare January 28, 2026 19:20
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex review is not enabled for this repo. Please contact the admins of this repo to enable Codex.

Copy link
Member Author

@claude review

@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch 7 times, most recently from 937a420 to 033b112 Compare January 29, 2026 19:49
Copy link
Member Author

@BugBot review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from 033b112 to 5420511 Compare January 29, 2026 22:48
@shuyangli shuyangli changed the base branch from main to sl/extract-json-escape January 29, 2026 22:50
@shuyangli shuyangli changed the base branch from sl/extract-json-escape to main January 29, 2026 22:51
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch 3 times, most recently from b1f86f3 to aabceaa Compare January 29, 2026 23:28
Uncomment tests

Implement delegating trait

Push order and limit down to postgres
@shuyangli shuyangli force-pushed the sl/postgres-inference-queries branch from aabceaa to 4d4abc2 Compare January 29, 2026 23:50

fn apply_demonstration_feedback_filter(
query_builder: &mut QueryBuilder<sqlx::Postgres>,
df: &crate::endpoints::stored_inferences::v1::types::DemonstrationFeedbackFilter,
Copy link
Member

Choose a reason for hiding this comment

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

import

Comment on lines +122 to +130
// Use EXISTS subquery to filter by metric value
query_builder
.push("EXISTS (SELECT 1 FROM tensorzero.boolean_metric_feedback f WHERE f.target_id = ");
query_builder.push(join_column);
query_builder.push(" AND f.metric_name = ");
query_builder.push_bind(bm.metric_name.clone());
query_builder.push(" AND f.value = ");
query_builder.push_bind(bm.value);
query_builder.push(")");
Copy link
Member

Choose a reason for hiding this comment

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

assuming this type of predicate is indexed is this fast?

let operator = tag.comparison_operator.to_postgres_operator();

// For Postgres JSONB, we use the ->> operator to extract text and compare
// We also check that the key exists to handle != correctly
Copy link
Member

Choose a reason for hiding this comment

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

let's update the comment to say what the correct handling is (the tag must exist with a different value)

Comment on lines +283 to +288
SELECT DISTINCT ON (target_id)
target_id,
value
FROM {table_name}
WHERE metric_name = '{metric_name}'
ORDER BY target_id, created_at DESC
Copy link
Member

Choose a reason for hiding this comment

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

will PG know to push down the join into this predicate?

// Convert JSONB to string
Ok(result.map(|r| r.output.to_string()))
}
}
Copy link
Member

Choose a reason for hiding this comment

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

aren't we just gonna re-parse on the outside of this method?

Comment on lines +537 to +550
let input_json = serde_json::to_value(&row.input).unwrap_or_default();
let output_json = serde_json::to_value(&row.output).unwrap_or_default();
let inference_params_json =
serde_json::to_value(&row.inference_params).unwrap_or_default();
let tags_json = serde_json::to_value(&row.tags).unwrap_or_default();
let extra_body_json = serde_json::to_value(&row.extra_body).unwrap_or_default();

let SerializedToolParams {
dynamic_tools,
dynamic_provider_tools,
allowed_tools,
tool_choice,
parallel_tool_calls,
} = serialize_tool_params(row.tool_params.as_ref()).unwrap_or_default();
Copy link
Member

Choose a reason for hiding this comment

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

all these unwrap_or_defaults() are bad. Would like to propagate errors if possible vs failing silently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants