Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
111 changes: 102 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ One Rust binary and one Postgres. Full-text search is embedded in the binary, ve
| **Entity resolution and review** | Duplicates are resolved in three stages: exact name or alias, embedding similarity, then a model's call on the doubtful pairs. Every merge can be undone. Uncertain cases go to a review queue: low-confidence extractions, suspected duplicates and cardinality conflicts. |
| **Reasoning and derivation** | Ontology axioms compile into rules: transitivity, symmetry, inverses and relation hierarchy derive new facts by forward chaining. Derivation is off by default, since a wrong axiom derives wrong facts. A derived fact is marked as such on the graph, carries validity and confidence like any other, and shows what it was derived from. When it contradicts an asserted fact, the asserted one stands. |
| **Conflict detection** | Three kinds of conflict, three sets of choices. A new fact that clashes with an older one: close the old, keep both, or reject the new. Data that breaks an axiom (self-loop, asymmetry, transitive cycle, cardinality): retract the fact, relax the axiom, or accept both. The ontology itself is checked first, because violations of a self-contradictory ontology are noise. |
| **Ontology-driven querying** | Mount a Postgres database on a base and chat can query it alongside the documents. The agent proposes how its tables map onto the ontology, and you confirm. The method behind it, [Ontology2SQL](https://github.com/deeplethe/ontology2sql), is state of the art on BIRD Mini-Dev for SQLite and PostgreSQL ([submission](https://github.com/bird-bench/bird-bench.github.io/pull/218)). |
| **Ontology-driven querying** | Mount a database on a base (Postgres, Trino for Iceberg / Delta Lake / Hive, Databricks, Snowflake) and chat can query it alongside the documents. The agent proposes how its tables map onto the ontology, and you confirm. The method behind it, [Ontology2SQL](https://github.com/deeplethe/ontology2sql), is state of the art on BIRD Mini-Dev for SQLite and PostgreSQL ([submission](https://github.com/bird-bench/bird-bench.github.io/pull/218)). |
| **Multi-user and permissions** | Each knowledge base has its own members and roles: owner, admin, editor and viewer. Open bases are readable by everyone in the deployment, restricted ones only by invitation. The first account registered becomes the system administrator. |
| **Decision ledger** | Confirming or rejecting a fact, merging or reverting an entity, rebuilding the graph: each leaves a record of who, when, and what the object looked like at the time. The ledger is append-only, and a record outlives its object, even the base it belonged to. |
| **[Decision intelligence (in development)](#roadmap)** | Record a decision, replay both what was understood and the course it took, and reason over overlaid scenarios. |
Expand Down Expand Up @@ -103,7 +103,7 @@ cd web && pnpm install && pnpm dev

- [ ] **Decision reasoning**: constraint computation, and replaying a decision after the fact
- [ ] **Execution gate**: checking an agent's calls against ontology rules and symbolic logic
- [ ] **Lakehouse for mapping and querying**: mapping exploration and Ontology2SQL over Iceberg / Delta Lake, Databricks, Snowflake and MaxCompute
- [ ] **MaxCompute**: mapping exploration and Ontology2SQL over Alibaba Cloud MaxCompute (Iceberg / Delta Lake via Trino, Databricks and Snowflake are in, awaiting a run against a real cluster)
- [ ] **More sources**: MySQL, ClickHouse and Doris drivers; S3, WebDAV, Notion and Feishu connectors
- [ ] **Time to the moment**: an `instant` precision beside year / month / day, for sources that carry a real timestamp. Today a connector rounds it to a UTC day, which can shift an event across midnight by one day
- [ ] **Agent memory over MCP**: episode writes, the retrieve endpoint, and the MCP server
Expand Down
6 changes: 6 additions & 0 deletions crates/utopia-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ async-trait = "0.1.92"
sqlparser = "0.62.0"
object_store = { version = "0.14.1", features = ["aws", "azure", "gcp"] }
quick-xml.workspace = true
url = "2.5.8"
percent-encoding = "2.3.2"
base64 = "0.23.1"

[dev-dependencies]
wiremock = "0.6.5"
4 changes: 2 additions & 2 deletions crates/utopia-server/src/api/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fn tools_schema(can_write: bool, data_source_names: &[String]) -> serde_json::Va
},
"sql": {
"type": "string",
"description": "One SELECT/WITH statement (PostgreSQL dialect)."
"description": "One SELECT/WITH statement in the source's own SQL dialect (PostgreSQL, Trino, Databricks or Snowflake; the schema document names the engine)."
},
"purpose": {
"type": "string",
Expand Down Expand Up @@ -435,7 +435,7 @@ pub async fn chat(
};
if !ds_names.is_empty() {
system_prompt.push_str(&format!(
"\nData: query_data runs read-only SQL (PostgreSQL dialect) against: {}. \
"\nData: query_data runs read-only SQL (in each source's own dialect) against: {}. \
For questions about numbers/metrics, search for the source's schema document \
first, then query. State units and the time range you used in the answer.",
ds_names.join(", ")
Expand Down
19 changes: 17 additions & 2 deletions crates/utopia-server/src/api/datasource_routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,25 @@ pub async fn create(
Json(body): Json<CreateBody>,
) -> ApiResult<Json<serde_json::Value>> {
require_admin(&user)?;
// 引擎跟着 scheme 走,界面只有一个连接串输入框;body.engine 只为兼容旧调用留着
let engine = crate::query_engine::engine_from_conn(&body.conn_string).ok_or_else(|| {
utopia_core::AppError::invalid(
"unsupported_conn_scheme",
format!(
"Connection string must start with one of: postgres://, trino://, databricks://, snowflake:// (engines: {})",
crate::query_engine::ENGINES.join(", ")
),
)
})?;
let _ = &body.engine;
// 连接串的形状在登记时就校验(缺令牌、缺 warehouse……),错误信息里带写法;
// 否则要等到「测试」才知道,而那一步只回 ok:false
crate::query_engine::engine_for(engine, &body.conn_string)
.map_err(|e| utopia_core::AppError::invalid("bad_conn_string", e.to_string()))?;
let id = utopia_store::datasources::create(
&state.pool,
&body.name,
&body.engine,
engine,
&body.conn_string,
user.id,
)
Expand Down Expand Up @@ -228,7 +243,7 @@ async fn sync_schema_doc(state: &AppState, kb_id: Uuid, ds_id: Uuid) -> anyhow::
.await?;

let mut md = format!(
"# Data source: {name}\n\nTables and columns available for SQL queries against this source.\n"
"# Data source: {name}\n\nEngine: {engine}. Tables and columns available for SQL queries against this source; write SQL in this engine's dialect.\n"
);
let mut current = String::new();
let mut tables = 0usize;
Expand Down
3 changes: 2 additions & 1 deletion crates/utopia-server/src/api/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,9 @@ pub(super) fn charter_source_json(n: usize, h: &utopia_search::DocsSection) -> s

/// 问数执行:安全闸(解析白名单)→ 引擎执行(只读会话 + 强制 LIMIT + 超时)→ JSON 行。
async fn run_query(state: &AppState, ds_id: Uuid, sql: &str) -> anyhow::Result<String> {
let guarded = crate::query_engine::guard_sql(sql)?;
let (engine, conn) = utopia_store::datasources::engine_and_conn(&state.pool, ds_id).await?;
// 闸门按引擎选方言:Databricks 的反引号、Snowflake 的 :: 转型都得先过得了解析
let guarded = crate::query_engine::guard_sql_for(&engine, sql)?;
let result = crate::query_engine::engine_for(&engine, &conn)?
.execute(&guarded)
.await?;
Expand Down
Loading
Loading