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
12 changes: 12 additions & 0 deletions crates/ov_cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,18 @@ impl HttpClient {
uri: String,
node_limit: i32,
threshold: Option<f64>,
since: Option<String>,
until: Option<String>,
time_field: Option<String>,
) -> Result<serde_json::Value> {
let body = serde_json::json!({
"query": query,
"target_uri": uri,
"limit": node_limit,
"score_threshold": threshold,
"since": since,
"until": until,
"time_field": time_field,
});
self.post("/api/v1/search/find", &body).await
}
Expand All @@ -514,13 +520,19 @@ impl HttpClient {
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
since: Option<String>,
until: Option<String>,
time_field: Option<String>,
) -> Result<serde_json::Value> {
let body = serde_json::json!({
"query": query,
"target_uri": uri,
"session_id": session_id,
"limit": node_limit,
"score_threshold": threshold,
"since": since,
"until": until,
"time_field": time_field,
});
self.post("/api/v1/search/search", &body).await
}
Expand Down
20 changes: 18 additions & 2 deletions crates/ov_cli/src/commands/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@ pub async fn find(
uri: &str,
node_limit: i32,
threshold: Option<f64>,
since: Option<&str>,
until: Option<&str>,
time_field: Option<&str>,
output_format: OutputFormat,
compact: bool,
) -> Result<()> {
let result = client
.find(query.to_string(), uri.to_string(), node_limit, threshold)
.find(
query.to_string(),
uri.to_string(),
node_limit,
threshold,
since.map(|s| s.to_string()),
until.map(|s| s.to_string()),
time_field.map(|s| s.to_string()),
)
.await?;
output_success(&result, output_format, compact);
Ok(())
Expand All @@ -25,6 +36,9 @@ pub async fn search(
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
since: Option<&str>,
until: Option<&str>,
time_field: Option<&str>,
output_format: OutputFormat,
compact: bool,
) -> Result<()> {
Expand All @@ -35,12 +49,14 @@ pub async fn search(
session_id,
node_limit,
threshold,
since.map(|s| s.to_string()),
until.map(|s| s.to_string()),
time_field.map(|s| s.to_string()),
)
.await?;
output_success(&result, output_format, compact);
Ok(())
}

pub async fn grep(
client: &HttpClient,
uri: &str,
Expand Down
61 changes: 59 additions & 2 deletions crates/ov_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ enum Commands {
/// Score threshold
#[arg(short, long)]
threshold: Option<f64>,
/// Only include results on or after this time (e.g. 48h, 7d, 2026-03-10, ISO-8601)
#[arg(long = "after")]
after: Option<String>,
/// Only include results on or before this time (e.g. 24h, 2026-03-15, ISO-8601)
#[arg(long = "before")]
before: Option<String>,
},
/// Run context-aware retrieval
Search {
Expand All @@ -402,6 +408,12 @@ enum Commands {
/// Score threshold
#[arg(short, long)]
threshold: Option<f64>,
/// Only include results on or after this time (e.g. 48h, 7d, 2026-03-10, ISO-8601)
#[arg(long = "after")]
after: Option<String>,
/// Only include results on or before this time (e.g. 24h, 2026-03-15, ISO-8601)
#[arg(long = "before")]
before: Option<String>,
},
/// Run content pattern search
Grep {
Expand Down Expand Up @@ -799,14 +811,23 @@ async fn main() {
uri,
node_limit,
threshold,
} => handle_find(query, uri, node_limit, threshold, ctx).await,
after,
before,
} => handle_find(query, uri, node_limit, threshold, after, before, ctx).await,
Commands::Search {
query,
uri,
session_id,
node_limit,
threshold,
} => handle_search(query, uri, session_id, node_limit, threshold, ctx).await,
after,
before,
} => {
handle_search(
query, uri, session_id, node_limit, threshold, after, before, ctx,
)
.await
}
Commands::Grep {
uri,
exclude_uri,
Expand Down Expand Up @@ -1297,12 +1318,15 @@ async fn handle_find(
uri: String,
node_limit: i32,
threshold: Option<f64>,
after: Option<String>,
before: Option<String>,
ctx: CliContext,
) -> Result<()> {
let mut params = vec![format!("--uri={}", uri), format!("-n {}", node_limit)];
if let Some(t) = threshold {
params.push(format!("--threshold {}", t));
}
append_time_filter_params(&mut params, after.as_deref(), before.as_deref());
params.push(format!("\"{}\"", query));
print_command_echo("ov find", &params.join(" "), ctx.config.echo_command);
let client = ctx.get_client();
Expand All @@ -1312,6 +1336,9 @@ async fn handle_find(
&uri,
node_limit,
threshold,
after.as_deref(),
before.as_deref(),
None,
ctx.output_format,
ctx.compact,
)
Expand All @@ -1324,6 +1351,8 @@ async fn handle_search(
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
after: Option<String>,
before: Option<String>,
ctx: CliContext,
) -> Result<()> {
let mut params = vec![format!("--uri={}", uri), format!("-n {}", node_limit)];
Expand All @@ -1333,6 +1362,7 @@ async fn handle_search(
if let Some(t) = threshold {
params.push(format!("--threshold {}", t));
}
append_time_filter_params(&mut params, after.as_deref(), before.as_deref());
params.push(format!("\"{}\"", query));
print_command_echo("ov search", &params.join(" "), ctx.config.echo_command);
let client = ctx.get_client();
Expand All @@ -1343,12 +1373,28 @@ async fn handle_search(
session_id,
node_limit,
threshold,
after.as_deref(),
before.as_deref(),
None,
ctx.output_format,
ctx.compact,
)
.await
}

fn append_time_filter_params(
params: &mut Vec<String>,
after: Option<&str>,
before: Option<&str>,
) {
if let Some(value) = after {
params.push(format!("--after {}", value));
}
if let Some(value) = before {
params.push(format!("--before {}", value));
}
}

/// Print command with specified parameters for debugging
fn print_command_echo(command: &str, params: &str, echo_enabled: bool) {
if echo_enabled {
Expand Down Expand Up @@ -1612,4 +1658,15 @@ mod tests {

assert!(result.is_err(), "removed write flags should not parse");
}

#[test]
fn append_time_filter_params_only_emits_after_and_before() {
let mut params = Vec::new();
let after = Some("7d".to_string());
let before = Some("2026-03-12".to_string());

super::append_time_filter_params(&mut params, after.as_deref(), before.as_deref());

assert_eq!(params, vec!["--after 7d", "--before 2026-03-12"]);
}
}
24 changes: 23 additions & 1 deletion docs/en/api/06-retrieval.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Basic vector similarity search.
| limit | int | No | 10 | Maximum number of results |
| score_threshold | float | No | None | Minimum relevance score threshold |
| filter | Dict | No | None | Metadata filters |
| since | str | No | None | Lower time bound, accepts `2h` or ISO 8601 / `YYYY-MM-DD`, timezone-less values use local time. CLI `--after` maps to this field |
| until | str | No | None | Upper time bound, accepts `30m` or ISO 8601 / `YYYY-MM-DD`, timezone-less values use local time. CLI `--before` maps to this field |
| time_field | `"updated_at"` or `"created_at"` | No | `"updated_at"` | Metadata time field used by `since` / `until` |

**FindResult Structure**

Expand Down Expand Up @@ -59,6 +62,13 @@ class MatchedContext:
```python
results = client.find("how to authenticate users")

recent_emails = client.find(
"invoice",
target_uri="viking://resources/email/",
since="7d",
time_field="created_at",
)

for ctx in results.resources:
print(f"URI: {ctx.uri}")
print(f"Score: {ctx.score:.3f}")
Expand Down Expand Up @@ -87,8 +97,11 @@ curl -X POST http://localhost:1933/api/v1/search/find \

```bash
openviking find "how to authenticate users" [--uri viking://resources/] [--limit 10]
openviking find "invoice" --after 7d
```

`--after` maps to API `since`, and `--before` maps to API `until`.

**Response**

```json
Expand Down Expand Up @@ -184,6 +197,9 @@ Search with session context and intent analysis.
| limit | int | No | 10 | Maximum number of results |
| score_threshold | float | No | None | Minimum relevance score threshold |
| filter | Dict | No | None | Metadata filters |
| since | str | No | None | Lower time bound, accepts `2h` or ISO 8601 / `YYYY-MM-DD`, timezone-less values use local time. CLI `--after` maps to this field |
| until | str | No | None | Upper time bound, accepts `30m` or ISO 8601 / `YYYY-MM-DD`, timezone-less values use local time. CLI `--before` maps to this field |
| time_field | `"updated_at"` or `"created_at"` | No | `"updated_at"` | Metadata time field used by `since` / `until` |

**Python SDK (Embedded / HTTP)**

Expand All @@ -202,7 +218,8 @@ session.add_message("assistant", [
# Search understands the conversation context
results = client.search(
"best practices",
session=session
session=session,
since="2h"
)

for ctx in results.resources:
Expand All @@ -223,6 +240,8 @@ curl -X POST http://localhost:1933/api/v1/search/search \
-d '{
"query": "best practices",
"session_id": "abc123",
"since": "2h",
"time_field": "updated_at",
"limit": 10
}'
```
Expand All @@ -231,8 +250,11 @@ curl -X POST http://localhost:1933/api/v1/search/search \

```bash
openviking search "best practices" [--session-id abc123] [--limit 10]
openviking search "watch vs scheduled" --after 2026-03-15 --before 2026-03-15
```

`--after` maps to API `since`, and `--before` maps to API `until`.

**Response**

```json
Expand Down
24 changes: 23 additions & 1 deletion docs/zh/api/06-retrieval.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ OpenViking 提供两种搜索方法:`find` 用于简单的语义搜索,`sear
| limit | int | 否 | 10 | 最大返回结果数 |
| score_threshold | float | 否 | None | 最低相关性分数阈值 |
| filter | Dict | 否 | None | 元数据过滤器 |
| since | str | 否 | None | 时间下界,支持 `2h` 或 ISO 8601 / `YYYY-MM-DD`,不带时区的值按本地时间解释。CLI `--after` 会映射到这个字段 |
| until | str | 否 | None | 时间上界,支持 `30m` 或 ISO 8601 / `YYYY-MM-DD`,不带时区的值按本地时间解释。CLI `--before` 会映射到这个字段 |
| time_field | `"updated_at"` 或 `"created_at"` | 否 | `"updated_at"` | `since` / `until` 使用的元数据时间字段 |

**FindResult 结构**

Expand Down Expand Up @@ -59,6 +62,13 @@ class MatchedContext:
```python
results = client.find("how to authenticate users")

recent_emails = client.find(
"invoice",
target_uri="viking://resources/email/",
since="7d",
time_field="created_at",
)

for ctx in results.resources:
print(f"URI: {ctx.uri}")
print(f"Score: {ctx.score:.3f}")
Expand Down Expand Up @@ -87,8 +97,11 @@ curl -X POST http://localhost:1933/api/v1/search/find \

```bash
openviking find "how to authenticate users" [--uri viking://resources/] [--limit 10]
openviking find "invoice" --after 7d
```

`--after` 会映射为 API `since`,`--before` 会映射为 API `until`。

**响应**

```json
Expand Down Expand Up @@ -184,6 +197,9 @@ curl -X POST http://localhost:1933/api/v1/search/find \
| limit | int | 否 | 10 | 最大返回结果数 |
| score_threshold | float | 否 | None | 最低相关性分数阈值 |
| filter | Dict | 否 | None | 元数据过滤器 |
| since | str | 否 | None | 时间下界,支持 `2h` 或 ISO 8601 / `YYYY-MM-DD`,不带时区的值按本地时间解释。CLI `--after` 会映射到这个字段 |
| until | str | 否 | None | 时间上界,支持 `30m` 或 ISO 8601 / `YYYY-MM-DD`,不带时区的值按本地时间解释。CLI `--before` 会映射到这个字段 |
| time_field | `"updated_at"` 或 `"created_at"` | 否 | `"updated_at"` | `since` / `until` 使用的元数据时间字段 |

**Python SDK (Embedded / HTTP)**

Expand All @@ -202,7 +218,8 @@ session.add_message("assistant", [
# 搜索能够理解对话上下文
results = client.search(
"best practices",
session=session
session=session,
since="2h"
)

for ctx in results.resources:
Expand All @@ -223,6 +240,8 @@ curl -X POST http://localhost:1933/api/v1/search/search \
-d '{
"query": "best practices",
"session_id": "abc123",
"since": "2h",
"time_field": "updated_at",
"limit": 10
}'
```
Expand All @@ -231,8 +250,11 @@ curl -X POST http://localhost:1933/api/v1/search/search \

```bash
openviking search "best practices" [--session-id abc123] [--limit 10]
openviking search "watch vs scheduled" --after 2026-03-15 --before 2026-03-15
```

`--after` 会映射为 API `since`,`--before` 会映射为 API `until`。

**响应**

```json
Expand Down
Loading
Loading