Skip to content
Draft
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
30 changes: 29 additions & 1 deletion crates/ov_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,14 +460,42 @@ async fn main() {
}

async fn handle_add_resource(
path: String,
mut path: String,
to: Option<String>,
reason: String,
instruction: String,
wait: bool,
timeout: Option<f64>,
ctx: CliContext,
) -> Result<()> {
// Validate path: if it's a local path, check if it exists
if !path.starts_with("http://") && !path.starts_with("https://") {
use std::path::Path;

// Unescape path: replace backslash followed by space with just space
let unescaped_path = path.replace("\\ ", " ");
let path_obj = Path::new(&unescaped_path);
if !path_obj.exists() {
eprintln!("Error: Path '{}' does not exist.", path);

// Check if there might be unquoted spaces
use std::env;
let args: Vec<String> = env::args().collect();

if let Some(add_resource_pos) = args.iter().position(|s| s == "add-resource" || s == "add") {
if args.len() > add_resource_pos + 2 {
let extra_args = &args[add_resource_pos + 2..];
let suggested_path = format!("{} {}", path, extra_args.join(" "));
eprintln!("\nIt looks like you may have forgotten to quote a path with spaces.");
eprintln!("Suggested command: ov add-resource \"{}\"", suggested_path);
}
}

std::process::exit(1);
}
path = unescaped_path;
}

let client = ctx.get_client();
commands::resources::add_resource(
&client, &path, to, reason, instruction, wait, timeout, ctx.output_format, ctx.compact
Expand Down
3 changes: 2 additions & 1 deletion examples/chatmem/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"api_base" : "https://ark-cn-beijing.bytedance.net/api/v3",
"api_key" : "not_gonna_give_u_this",
"backend" : "volcengine",
"model" : "doubao-seed-1-8-251228"
"model" : "doubao-seed-1-8-251228",
"thinking": false
}
}
3 changes: 2 additions & 1 deletion examples/mcp-query/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"api_base" : "https://ark-cn-beijing.bytedance.net/api/v3",
"api_key" : "<your-api-key>",
"provider" : "volcengine",
"model" : "doubao-seed-1-8-251228"
"model" : "doubao-seed-1-8-251228",
"thinking": false
}
}
3 changes: 2 additions & 1 deletion examples/memex/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"api_base" : "https://ark.cn-beijing.volces.com/api/v3",
"api_key" : "your-volcengine-api-key",
"backend" : "volcengine",
"model" : "doubao-seed-1-8-251228"
"model" : "doubao-seed-1-8-251228",
"thinking": false
}
}
3 changes: 2 additions & 1 deletion examples/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"api_base": "https://ark.cn-beijing.volces.com/api/v3",
"temperature": 0.0,
"max_retries": 2,
"provider": "volcengine"
"provider": "volcengine",
"thinking": false
},
"rerank": {
"ak": null,
Expand Down
3 changes: 2 additions & 1 deletion examples/query/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"api_base" : "https://ark-cn-beijing.bytedance.net/api/v3",
"api_key" : "not_gonna_give_u_this",
"provider" : "volcengine",
"model" : "doubao-seed-1-8-251228"
"model" : "doubao-seed-1-8-251228",
"thinking": false
}
}
3 changes: 2 additions & 1 deletion examples/server_client/ov.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"api_base": "https://ark.cn-beijing.volces.com/api/v3",
"temperature": 0.0,
"max_retries": 2,
"provider": "volcengine"
"provider": "volcengine",
"thinking": false
}
}
26 changes: 23 additions & 3 deletions openviking/models/vlm/backends/litellm_vlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,31 @@ def _build_kwargs(self, model: str, messages: list) -> dict[str, Any]:

return kwargs

def get_completion(self, prompt: str) -> str:
def get_completion(self, prompt: str, thinking: bool = False) -> str:
"""Get text completion synchronously."""
model = self._resolve_model(self.model or "gpt-4o-mini")
messages = [{"role": "user", "content": prompt}]
original_thinking = self._thinking
if thinking:
self._thinking = thinking
kwargs = self._build_kwargs(model, messages)
self._thinking = original_thinking

response = completion(**kwargs)
self._update_token_usage_from_response(response)
return response.choices[0].message.content or ""

async def get_completion_async(self, prompt: str, max_retries: int = 0) -> str:
async def get_completion_async(
self, prompt: str, thinking: bool = False, max_retries: int = 0
) -> str:
"""Get text completion asynchronously."""
model = self._resolve_model(self.model or "gpt-4o-mini")
messages = [{"role": "user", "content": prompt}]
original_thinking = self._thinking
if thinking:
self._thinking = thinking
kwargs = self._build_kwargs(model, messages)
self._thinking = original_thinking

last_error = None
for attempt in range(max_retries + 1):
Expand All @@ -164,7 +174,7 @@ async def get_completion_async(self, prompt: str, max_retries: int = 0) -> str:
except Exception as e:
last_error = e
if attempt < max_retries:
await asyncio.sleep(2 ** attempt)
await asyncio.sleep(2**attempt)

if last_error:
raise last_error
Expand All @@ -174,6 +184,7 @@ def get_vision_completion(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion synchronously."""
model = self._resolve_model(self.model or "gpt-4o-mini")
Expand All @@ -184,7 +195,11 @@ def get_vision_completion(
content.append({"type": "text", "text": prompt})

messages = [{"role": "user", "content": content}]
original_thinking = self._thinking
if thinking:
self._thinking = thinking
kwargs = self._build_kwargs(model, messages)
self._thinking = original_thinking

response = completion(**kwargs)
self._update_token_usage_from_response(response)
Expand All @@ -194,6 +209,7 @@ async def get_vision_completion_async(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion asynchronously."""
model = self._resolve_model(self.model or "gpt-4o-mini")
Expand All @@ -204,7 +220,11 @@ async def get_vision_completion_async(
content.append({"type": "text", "text": prompt})

messages = [{"role": "user", "content": content}]
original_thinking = self._thinking
if thinking:
self._thinking = thinking
kwargs = self._build_kwargs(model, messages)
self._thinking = original_thinking

response = await acompletion(**kwargs)
self._update_token_usage_from_response(response)
Expand Down
32 changes: 22 additions & 10 deletions openviking/models/vlm/backends/openai_vlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def get_vision_completion(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion"""
client = self.get_client()
Expand All @@ -140,18 +141,24 @@ def get_vision_completion(
content.append(self._prepare_image(img))
content.append({"type": "text", "text": prompt})

response = client.chat.completions.create(
model=self.model or "gpt-4o-mini",
messages=[{"role": "user", "content": content}],
temperature=self.temperature,
)
kwargs = {
"model": self.model or "gpt-4o-mini",
"messages": [{"role": "user", "content": content}],
"temperature": self.temperature,
}

if self.provider == "volcengine":
kwargs["thinking"] = {"type": "disabled" if not thinking else "enabled"}

response = client.chat.completions.create(**kwargs)
self._update_token_usage_from_response(response)
return response.choices[0].message.content or ""

async def get_vision_completion_async(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion asynchronously"""
client = self.get_async_client()
Expand All @@ -161,10 +168,15 @@ async def get_vision_completion_async(
content.append(self._prepare_image(img))
content.append({"type": "text", "text": prompt})

response = await client.chat.completions.create(
model=self.model or "gpt-4o-mini",
messages=[{"role": "user", "content": content}],
temperature=self.temperature,
)
kwargs = {
"model": self.model or "gpt-4o-mini",
"messages": [{"role": "user", "content": content}],
"temperature": self.temperature,
}

if self.provider == "volcengine":
kwargs["thinking"] = {"type": "disabled" if not thinking else "enabled"}

response = await client.chat.completions.create(**kwargs)
self._update_token_usage_from_response(response)
return response.choices[0].message.content or ""
16 changes: 10 additions & 6 deletions openviking/models/vlm/backends/volcengine_vlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,26 @@ def get_async_client(self):
)
return self._async_client

def get_completion(self, prompt: str) -> str:
return super().get_completion(prompt)
def get_completion(self, prompt: str, thinking: bool = False) -> str:
return super().get_completion(prompt, thinking)

async def get_completion_async(self, prompt: str, max_retries: int = 0) -> str:
return await super().get_completion_async(prompt, max_retries)
async def get_completion_async(
self, prompt: str, thinking: bool = False, max_retries: int = 0
) -> str:
return await super().get_completion_async(prompt, thinking, max_retries)

def get_vision_completion(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
return super().get_vision_completion(prompt, images)
return super().get_vision_completion(prompt, images, thinking)

async def get_vision_completion_async(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
return await super().get_vision_completion_async(prompt, images)
return await super().get_vision_completion_async(prompt, images, thinking)
12 changes: 10 additions & 2 deletions openviking/models/vlm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ def __init__(self, config: Dict[str, Any]):
self._token_tracker = TokenUsageTracker()

@abstractmethod
def get_completion(self, prompt: str) -> str:
def get_completion(self, prompt: str, thinking: bool = False) -> str:
"""Get text completion"""
pass

@abstractmethod
async def get_completion_async(self, prompt: str, max_retries: int = 0) -> str:
async def get_completion_async(
self, prompt: str, thinking: bool = False, max_retries: int = 0
) -> str:
"""Get text completion asynchronously"""
pass

Expand All @@ -41,6 +43,7 @@ def get_vision_completion(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion"""
pass
Expand All @@ -50,6 +53,7 @@ async def get_vision_completion_async(
self,
prompt: str,
images: List[Union[str, Path, bytes]],
thinking: bool = False,
) -> str:
"""Get vision completion asynchronously"""
pass
Expand Down Expand Up @@ -128,16 +132,20 @@ def create(config: Dict[str, Any]) -> VLMBase:
if not use_litellm:
if provider == "openai":
from .backends.openai_vlm import OpenAIVLM

return OpenAIVLM(config)
elif provider == "volcengine":
from .backends.volcengine_vlm import VolcEngineVLM

return VolcEngineVLM(config)

from .backends.litellm_vlm import LiteLLMVLMProvider

return LiteLLMVLMProvider(config)

@staticmethod
def get_available_providers() -> List[str]:
"""Get list of available providers"""
from .registry import get_all_provider_names

return get_all_provider_names()
Loading