Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7a6f574
feat: 新前端项目初始化
Ferry-200 Apr 5, 2026
662ef75
style: 切换到vega风格
Ferry-200 Apr 5, 2026
f59ef21
feat: 新增定制化的 openapi codegen
Ferry-200 Apr 5, 2026
da4eb4c
chore: 把openapi生成的ov-client移到src/gen底下
Ferry-200 Apr 5, 2026
dbe9425
feat: 生成 openviking-server 的 client
Ferry-200 Apr 5, 2026
312e006
feat: 搭建 openviking client 的适配层
Ferry-200 Apr 5, 2026
a4e820c
docs: 新增 ov-client 适配层描述
Ferry-200 Apr 5, 2026
907f55b
chore: 清理项目模板
Ferry-200 Apr 5, 2026
7330f0b
clean: 清理模板样式
Ferry-200 Apr 5, 2026
afc4bc4
deps: 新增 tanstack query 依赖
Ferry-200 Apr 5, 2026
04166ac
style: 调整 shadcn 风格
Ferry-200 Apr 5, 2026
08f7c32
feat: 导入基础的shadcn组件
Ferry-200 Apr 5, 2026
9a1f1db
feat: 复刻旧的 webconsole
Ferry-200 Apr 5, 2026
07c7d41
fix: 修复legacy代码commit不完整的问题
Ferry-200 Apr 5, 2026
ebf541b
docs: readme
Ferry-200 Apr 5, 2026
0479077
feat: 允许不显示传递自定义client
Ferry-200 Apr 5, 2026
b9ee46a
feat: 三段式布局 + 功能页拆分
Jye10032 Apr 6, 2026
1189cf9
dev: add development environment script
ifeichuan Apr 6, 2026
849e043
feat: support tags for resource management and retrieval
ifeichuan Apr 7, 2026
3ae8cb7
feat(search): support tags filtering and return tags in retrieval res…
ifeichuan Apr 7, 2026
c2f9bb7
refactor(tags): extract build_tags_filter helper and improve validation
ifeichuan Apr 7, 2026
de8f112
chore(scripts): add fast mode toggle to bootstrap menu
ifeichuan Apr 7, 2026
9cd6f9b
fix(tags): sanitize once and isolate semantic tags context
ifeichuan Apr 7, 2026
dc41b24
Merge pull request #1 from Jye10032/feat/sidebar-layout
Jye10032 Apr 7, 2026
78cc230
feat(web-studio): add i18n support and dark/light theme toggle
Jye10032 Apr 8, 2026
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ share/python-wheels/
*.egg
MANIFEST
openviking.egg-info/
data/

# Rust
target/
Expand Down
85 changes: 70 additions & 15 deletions crates/ov_cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use reqwest::{Client as ReqwestClient, StatusCode};
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::collections::HashSet;
use std::fs::File;
use std::path::Path;
use tempfile::{Builder, NamedTempFile};
Expand Down Expand Up @@ -477,20 +478,63 @@ impl HttpClient {

// ============ Search Methods ============

fn build_tags_filter(tags: &str) -> Result<Value> {
let mut tag_list: Vec<&str> = tags
.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect();

let mut seen = HashSet::new();
tag_list.retain(|s| seen.insert(*s));

if tag_list.is_empty() {
return Err(Error::Client(
"'tags' must contain at least one non-empty tag".to_string(),
));
}

let conds: Vec<Value> = tag_list
.into_iter()
.map(|s| {
serde_json::json!({
"op": "contains",
"field": "tags",
"substring": s
})
})
.collect();

Ok(if conds.len() == 1 {
conds[0].clone()
} else {
serde_json::json!({
"op": "and",
"conds": conds
})
})
}

pub async fn find(
&self,
query: String,
uri: String,
node_limit: i32,
threshold: Option<f64>,
tags: Option<String>,
) -> Result<serde_json::Value> {
let body = serde_json::json!({
"query": query,
"target_uri": uri,
"limit": node_limit,
"score_threshold": threshold,
});
self.post("/api/v1/search/find", &body).await
let mut body_map = serde_json::Map::new();
body_map.insert("query".to_string(), serde_json::json!(query));
body_map.insert("target_uri".to_string(), serde_json::json!(uri));
body_map.insert("limit".to_string(), serde_json::json!(node_limit));
if let Some(t) = threshold {
body_map.insert("score_threshold".to_string(), serde_json::json!(t));
}
if let Some(t) = tags {
let filter = Self::build_tags_filter(&t)?;
body_map.insert("filter".to_string(), filter);
}
self.post("/api/v1/search/find", &serde_json::Value::Object(body_map)).await
}

pub async fn search(
Expand All @@ -500,15 +544,23 @@ impl HttpClient {
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
tags: 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,
});
self.post("/api/v1/search/search", &body).await
let mut body_map = serde_json::Map::new();
body_map.insert("query".to_string(), serde_json::json!(query));
body_map.insert("target_uri".to_string(), serde_json::json!(uri));
if let Some(s) = session_id {
body_map.insert("session_id".to_string(), serde_json::json!(s));
}
body_map.insert("limit".to_string(), serde_json::json!(node_limit));
if let Some(t) = threshold {
body_map.insert("score_threshold".to_string(), serde_json::json!(t));
}
if let Some(t) = tags {
let filter = Self::build_tags_filter(&t)?;
body_map.insert("filter".to_string(), filter);
}
self.post("/api/v1/search/search", &serde_json::Value::Object(body_map)).await
}

pub async fn grep(
Expand Down Expand Up @@ -560,6 +612,7 @@ impl HttpClient {
exclude: Option<String>,
directly_upload_media: bool,
watch_interval: f64,
tags: Option<String>,
) -> Result<serde_json::Value> {
let path_obj = Path::new(path);

Expand Down Expand Up @@ -587,6 +640,7 @@ impl HttpClient {
"exclude": exclude,
"directly_upload_media": directly_upload_media,
"watch_interval": watch_interval,
"tags": tags,
});

self.post("/api/v1/resources", &body).await
Expand All @@ -607,6 +661,7 @@ impl HttpClient {
"exclude": exclude,
"directly_upload_media": directly_upload_media,
"watch_interval": watch_interval,
"tags": tags,
});

self.post("/api/v1/resources", &body).await
Expand Down
2 changes: 2 additions & 0 deletions crates/ov_cli/src/commands/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub async fn add_resource(
exclude: Option<String>,
directly_upload_media: bool,
watch_interval: f64,
tags: Option<String>,
format: OutputFormat,
compact: bool,
) -> Result<()> {
Expand All @@ -35,6 +36,7 @@ pub async fn add_resource(
exclude,
directly_upload_media,
watch_interval,
tags,
)
.await?;
output_success(&result, format, compact);
Expand Down
5 changes: 4 additions & 1 deletion crates/ov_cli/src/commands/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ pub async fn find(
uri: &str,
node_limit: i32,
threshold: Option<f64>,
tags: Option<String>,
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, tags)
.await?;
output_success(&result, output_format, compact);
Ok(())
Expand All @@ -25,6 +26,7 @@ pub async fn search(
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
tags: Option<String>,
output_format: OutputFormat,
compact: bool,
) -> Result<()> {
Expand All @@ -35,6 +37,7 @@ pub async fn search(
session_id,
node_limit,
threshold,
tags,
)
.await?;
output_success(&result, output_format, compact);
Expand Down
29 changes: 27 additions & 2 deletions crates/ov_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ enum Commands {
/// Watch interval in minutes for automatic resource monitoring (0 = no monitoring)
#[arg(long, default_value = "0")]
watch_interval: f64,
/// Tags for the resource (comma-separated)
#[arg(long)]
tags: Option<String>,
},
/// Add a skill into OpenViking
AddSkill {
Expand Down Expand Up @@ -377,6 +380,9 @@ enum Commands {
/// Score threshold
#[arg(short, long)]
threshold: Option<f64>,
/// Filter by tags (comma-separated)
#[arg(long)]
tags: Option<String>,
},
/// Run context-aware retrieval
Search {
Expand All @@ -399,6 +405,9 @@ enum Commands {
/// Score threshold
#[arg(short, long)]
threshold: Option<f64>,
/// Filter by tags (comma-separated)
#[arg(long)]
tags: Option<String>,
},
/// Run content pattern search
Grep {
Expand Down Expand Up @@ -666,6 +675,7 @@ async fn main() {
exclude,
no_directly_upload_media,
watch_interval,
tags,
} => {
handle_add_resource(
path,
Expand All @@ -681,6 +691,7 @@ async fn main() {
exclude,
no_directly_upload_media,
watch_interval,
tags,
ctx,
)
.await
Expand Down Expand Up @@ -794,14 +805,16 @@ async fn main() {
uri,
node_limit,
threshold,
} => handle_find(query, uri, node_limit, threshold, ctx).await,
tags,
} => handle_find(query, uri, node_limit, threshold, tags, ctx).await,
Commands::Search {
query,
uri,
session_id,
node_limit,
threshold,
} => handle_search(query, uri, session_id, node_limit, threshold, ctx).await,
tags,
} => handle_search(query, uri, session_id, node_limit, threshold, tags, ctx).await,
Commands::Grep {
uri,
exclude_uri,
Expand Down Expand Up @@ -837,6 +850,7 @@ async fn handle_add_resource(
exclude: Option<String>,
no_directly_upload_media: bool,
watch_interval: f64,
tags: Option<String>,
ctx: CliContext,
) -> Result<()> {
let is_url =
Expand Down Expand Up @@ -910,6 +924,7 @@ async fn handle_add_resource(
exclude,
directly_upload_media,
watch_interval,
tags,
ctx.output_format,
ctx.compact,
)
Expand Down Expand Up @@ -1271,12 +1286,16 @@ async fn handle_find(
uri: String,
node_limit: i32,
threshold: Option<f64>,
tags: 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));
}
if let Some(t) = &tags {
params.push(format!("--tags {}", t));
}
params.push(format!("\"{}\"", query));
print_command_echo("ov find", &params.join(" "), ctx.config.echo_command);
let client = ctx.get_client();
Expand All @@ -1286,6 +1305,7 @@ async fn handle_find(
&uri,
node_limit,
threshold,
tags,
ctx.output_format,
ctx.compact,
)
Expand All @@ -1298,6 +1318,7 @@ async fn handle_search(
session_id: Option<String>,
node_limit: i32,
threshold: Option<f64>,
tags: Option<String>,
ctx: CliContext,
) -> Result<()> {
let mut params = vec![format!("--uri={}", uri), format!("-n {}", node_limit)];
Expand All @@ -1307,6 +1328,9 @@ async fn handle_search(
if let Some(t) = threshold {
params.push(format!("--threshold {}", t));
}
if let Some(t) = &tags {
params.push(format!("--tags {}", t));
}
params.push(format!("\"{}\"", query));
print_command_echo("ov search", &params.join(" "), ctx.config.echo_command);
let client = ctx.get_client();
Expand All @@ -1317,6 +1341,7 @@ async fn handle_search(
session_id,
node_limit,
threshold,
tags,
ctx.output_format,
ctx.compact,
)
Expand Down
9 changes: 6 additions & 3 deletions docs/en/api/02-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Add a resource to the knowledge base.
| wait | bool | No | False | Wait for semantic processing to complete |
| timeout | float | No | None | Timeout in seconds (only used when wait=True) |
| watch_interval | float | No | 0 | Watch interval (minutes). >0 enables/updates watch; <=0 disables watch. Only takes effect when target is provided |
| tags | str | No | None | Comma-separated list of tags to attach to the resource |

**How local files and directories work**

Expand Down Expand Up @@ -75,7 +76,8 @@ When you call `add_resource()` repeatedly for the same resource URI, the system
```python
result = client.add_resource(
"./documents/guide.md",
reason="User guide documentation"
reason="User guide documentation",
tags="documentation,guide"
)
print(f"Added: {result['root_uri']}")

Expand All @@ -94,14 +96,15 @@ curl -X POST http://localhost:1933/api/v1/resources \
-H "X-API-Key: your-key" \
-d '{
"path": "https://example.com/guide.md",
"reason": "User guide documentation"
"reason": "User guide documentation",
"tags": "documentation,guide"
}'
```

**CLI**

```bash
openviking add-resource ./documents/guide.md --reason "User guide documentation"
openviking add-resource ./documents/guide.md --reason "User guide documentation" --tags "documentation,guide"
```

**Response**
Expand Down
3 changes: 2 additions & 1 deletion docs/en/api/03-filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ List directory contents.
"modTime": "2024-01-01T00:00:00Z", # ISO timestamp
"isDir": True, # True if directory
"uri": "viking://resources/docs/", # Viking URI
"meta": {} # Optional metadata
"meta": {}, # Optional metadata
"tags": "guide,api" # Tags (for display only. Filtering is supported in find/search)
}
```

Expand Down
3 changes: 3 additions & 0 deletions docs/en/api/06-retrieval.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ 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 |
| tags | str | No | None | Comma-separated list of tags to filter by (shortcut for filter) |

**FindResult Structure**

Expand All @@ -51,6 +52,7 @@ class MatchedContext:
category: str # Category
score: float # Relevance score (0-1)
match_reason: str # Why this matched
tags: Optional[str] # Comma-separated tags (if any)
relations: List[RelatedContext] # Related contexts
```

Expand Down Expand Up @@ -184,6 +186,7 @@ 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 |
| tags | str | No | None | Comma-separated list of tags to filter by (shortcut for filter) |

**Python SDK (Embedded / HTTP)**

Expand Down
Loading