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
31 changes: 25 additions & 6 deletions crates/ov_cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,14 @@ impl HttpClient {

let form = reqwest::multipart::Form::new().part("file", part);

let mut headers = self.build_headers();
// Remove Content-Type: application/json, let reqwest set multipart/form-data automatically
headers.remove(reqwest::header::CONTENT_TYPE);

let response = self
.http
.post(&url)
.headers(headers)
.multipart(form)
.send()
.await
Expand Down Expand Up @@ -460,12 +465,26 @@ impl HttpClient {
wait: bool,
timeout: Option<f64>,
) -> Result<serde_json::Value> {
let body = serde_json::json!({
"data": data,
"wait": wait,
"timeout": timeout,
});
self.post("/api/v1/skills", &body).await
let path_obj = Path::new(data);

if path_obj.exists() && path_obj.is_dir() && !self.is_local_server() {
let zip_file = self.zip_directory(path_obj)?;
let temp_path = self.upload_temp_file(zip_file.path()).await?;

let body = serde_json::json!({
"temp_path": temp_path,
"wait": wait,
"timeout": timeout,
});
self.post("/api/v1/skills", &body).await
} else {
let body = serde_json::json!({
"data": data,
"wait": wait,
"timeout": timeout,
});
self.post("/api/v1/skills", &body).await
}
}

// ============ Relation Methods ============
Expand Down
10 changes: 8 additions & 2 deletions openviking/server/routers/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class AddResourceRequest(BaseModel):
class AddSkillRequest(BaseModel):
"""Request model for add_skill."""

data: Any
data: Any = None
temp_path: Optional[str] = None
wait: bool = False
timeout: Optional[float] = None

Expand Down Expand Up @@ -108,8 +109,13 @@ async def add_skill(
):
"""Add skill to OpenViking."""
service = get_service()

data = request.data
if request.temp_path:
data = request.temp_path

result = await service.resources.add_skill(
data=request.data,
data=data,
ctx=_ctx,
wait=request.wait,
timeout=request.timeout,
Expand Down
11 changes: 9 additions & 2 deletions openviking/utils/skill_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
Handles skill parsing, LLM generation, and storage operations.
"""

import tempfile
import zipfile
from pathlib import Path
from typing import Any, Dict, List, Optional

Expand Down Expand Up @@ -116,11 +118,16 @@ def _parse_skill(self, data: Any) -> tuple[Dict[str, Any], List[Path], Optional[
auxiliary_files = []
base_path = None

# Convert string paths to Path objects
if isinstance(data, str):
path_obj = Path(data)
if path_obj.exists():
data = path_obj
if zipfile.is_zipfile(path_obj):
temp_dir = Path(tempfile.mkdtemp())
with zipfile.ZipFile(path_obj, "r") as zipf:
zipf.extractall(temp_dir)
data = temp_dir
else:
data = path_obj

if isinstance(data, Path):
if data.is_dir():
Expand Down
25 changes: 20 additions & 5 deletions openviking_cli/client/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,13 +314,28 @@ async def add_skill(
timeout: Optional[float] = None,
) -> Dict[str, Any]:
"""Add skill to OpenViking."""
request_data = {
"wait": wait,
"timeout": timeout,
}

if isinstance(data, str):
path_obj = Path(data)
if path_obj.exists() and path_obj.is_dir() and not self._is_local_server():
zip_path = self._zip_directory(data)
try:
temp_path = await self._upload_temp_file(zip_path)
request_data["temp_path"] = temp_path
finally:
Path(zip_path).unlink(missing_ok=True)
else:
request_data["data"] = data
else:
request_data["data"] = data

response = await self._http.post(
"/api/v1/skills",
json={
"data": data,
"wait": wait,
"timeout": timeout,
},
json=request_data,
)
return self._handle_response(response)

Expand Down
Loading