Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
639c107
docs: add memory extractor templating and update mechanism optimizati…
chenjw Mar 17, 2026
a75bc30
feat: add memory templating system with ReAct orchestrator
chenjw Mar 21, 2026
78987c6
refactor: memory extractor templating system with ReAct orchestrator
chenjw Mar 23, 2026
2175c1b
fix: pass ctx/user/session_id in commit_async for memory extraction
chenjw Mar 23, 2026
a071bfb
fix: convert FindResult to dict before returning from search tool
chenjw Mar 23, 2026
57d72ed
fix: swap None check before accessing final_operations in memory_react
chenjw Mar 24, 2026
98fd740
feat: add edit_overview support and optimize memory registry initiali…
chenjw Mar 24, 2026
c41bed6
fix: remove unnecessary indent in JSON schema output
chenjw Mar 24, 2026
707fd77
docs: add markdown link format hint to overview field description
chenjw Mar 24, 2026
3c6164d
feat: add pre-fetch search based on user messages in conversation
chenjw Mar 24, 2026
1e717af
rebase
chenjw Mar 24, 2026
53a8068
rebase
chenjw Mar 24, 2026
2cf3806
feat: add vectorization for memory files and overview format
chenjw Mar 25, 2026
4bb034c
docs: update bot README to use openviking-server --with-bot and ov chat
chenjw Mar 25, 2026
07f708e
test: rewrite xiaomei memory demo as standalone script
chenjw Mar 25, 2026
21027a3
rebase
chenjw Mar 25, 2026
6d41386
refactor: use markdown links in overview instead of numeric references
chenjw Mar 25, 2026
6f1e183
fix: remove duplicate code from copy-paste residue
chenjw Mar 25, 2026
d827e3c
update
chenjw Mar 25, 2026
099f980
rebase
chenjw Mar 25, 2026
a28f2e0
update
chenjw Mar 25, 2026
0755449
rebase
chenjw Mar 25, 2026
6421b11
update
chenjw Mar 25, 2026
0553c96
update
chenjw Mar 25, 2026
9ec2872
rebase
chenjw Mar 25, 2026
2aa43ee
update
chenjw Mar 25, 2026
3e98a44
update
chenjw Mar 26, 2026
717e81d
fix: VolcEngine VLM response parsing and caching issues
chenjw Mar 26, 2026
3a102d8
修复内存提取过程中锁获取失败的问题
chenjw Mar 26, 2026
bb07e66
修复内存提取过程中锁获取失败问题及隐藏 VolcEngineVLM 相关日志
chenjw Mar 26, 2026
a140413
修复内存提取过程中锁获取失败问题及隐藏 VolcEngineVLM 相关日志
chenjw Mar 27, 2026
3b245c3
实现通过 start_index 和 end_index 获取原文内容的功能
chenjw Mar 27, 2026
ec7b092
Improve start_index and end_index understanding by adding message ind…
chenjw Mar 27, 2026
b66c209
Update skills.yaml and tools.yaml to use Jinja2 template syntax
chenjw Mar 27, 2026
7a521b1
实现 events.yaml 记忆类型只新增模式
chenjw Mar 27, 2026
d9177c8
更新其他记忆类型配置和测试文件
chenjw Mar 27, 2026
1358ea7
优化测试输出,隐藏 cache_control 日志
chenjw Mar 27, 2026
6d196bd
优化工具记忆模板和压缩器v2
chenjw Mar 27, 2026
799eeaa
优化记忆提取:search结果总是加入messages,refetch时允许额外迭代
chenjw Mar 27, 2026
0c36539
重构记忆提取模块
chenjw Mar 27, 2026
dbb39c9
rebase
chenjw Mar 27, 2026
3308637
添加 FaultTolerantBaseModel,重构容错逻辑
chenjw Mar 28, 2026
b53ba8e
修复 extract_context 未定义问题
chenjw Mar 28, 2026
68e0a3b
修复 tools.yaml 模板变量缺失问题
chenjw Mar 28, 2026
8bac6e9
简化 events.yaml 模板
chenjw Mar 28, 2026
4fafecc
恢复 events.yaml extract_context 调用
chenjw Mar 28, 2026
1ce7c79
使用 DebugUndefined 处理模板未定义变量
chenjw Mar 28, 2026
1af3891
修复 MEMORY_FIELDS 出现在 abstract 中的问题
chenjw Mar 28, 2026
adf5801
修复向量索引时 MEMORY_FIELDS 出现在 abstract 中的问题
chenjw Mar 28, 2026
97ceb00
修复 MEMORY_FIELDS 在向量索引中出现的多个问题
chenjw Mar 28, 2026
3482453
支持 events ranges 单个索引格式
chenjw Mar 28, 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: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ logs/
*.temp
.tmp/
ov.conf
result/

# Jupyter Notebook
.ipynb_checkpoints
Expand Down
118 changes: 74 additions & 44 deletions bot/eval/locomo/import_to_ov.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@

import argparse
import json
import subprocess
import sys
import time
from datetime import datetime

import openviking as ov


def parse_test_file(path: str) -> list[dict]:
"""Parse txt test file into sessions.
Expand Down Expand Up @@ -47,30 +48,17 @@ def parse_test_file(path: str) -> list[dict]:
return sessions


def format_locomo_message(msg: dict) -> str:
"""Format a single LoCoMo message into a natural chat-style string.
def format_locomo_message(msg: dict, index: int | None = None) -> str:
"""Format a single LoCoMo message into chat-style string.
Output format:
Speaker: text here
image_url: caption
[index][Speaker]: text here
"""
speaker = msg.get("speaker", "unknown")
text = msg.get("text", "")
line = f"{speaker}: {text}"

img_urls = msg.get("img_url", [])
if isinstance(img_urls, str):
img_urls = [img_urls]
blip = msg.get("blip_caption", "")

if img_urls:
for url in img_urls:
caption = f": {blip}" if blip else ""
line += f"\n{url}{caption}"
elif blip:
line += f"\n({blip})"

return line
if index is not None:
return f"[{index}][{speaker}]: {text}"
return f"[{speaker}]: {text}"


def load_locomo_data(
Expand All @@ -93,9 +81,10 @@ def build_session_messages(
item: dict,
session_range: tuple[int, int] | None = None,
) -> list[dict]:
"""Build bundled session messages for one LoCoMo sample.
"""Build session messages for one LoCoMo sample.
Returns list of dicts with keys: message, meta.
Returns list of dicts with keys: messages, meta.
Each dict represents a session with multiple messages (user/assistant role).
"""
conv = item["conversation"]
speakers = f"{conv['speaker_a']} & {conv['speaker_b']}"
Expand All @@ -116,13 +105,20 @@ def build_session_messages(
dt_key = f"{sk}_date_time"
date_time = conv.get(dt_key, "")

parts = [f"[group chat conversation: {date_time}]"]
for msg in conv[sk]:
parts.append(format_locomo_message(msg))
combined = "\n\n".join(parts)
# Extract messages with all as user role, including speaker in content
messages = []
for idx, msg in enumerate(conv[sk]):
speaker = msg.get("speaker", "unknown")
text = msg.get("text", "")
messages.append({
"role": "user",
"text": f"[{speaker}]: {text}",
"speaker": speaker,
"index": idx
})

sessions.append({
"message": combined,
"messages": messages,
"meta": {
"sample_id": item["sample_id"],
"session_key": sk,
Expand All @@ -138,6 +134,7 @@ def build_session_messages(
# Ingest record helpers (avoid duplicate ingestion)
# ---------------------------------------------------------------------------


def load_ingest_record(record_path: str = ".ingest_record.json") -> dict:
"""Load existing ingest record file, return empty dict if not exists."""
try:
Expand Down Expand Up @@ -182,21 +179,43 @@ def mark_ingested(
# OpenViking import
# ---------------------------------------------------------------------------

def viking_ingest(msg: str) -> None:
"""Save a message to OpenViking via `ov add-memory`."""
result = subprocess.run(
["ov", "add-memory", msg],
capture_output=True,
text=True,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.strip() or f"ov exited with code {result.returncode}")

def viking_ingest(messages: list[dict]) -> None:
"""Save messages to OpenViking via SyncHTTPClient (add messages + commit session)."""
client = ov.SyncHTTPClient()
client.initialize()

# Create new session
session_result = client.create_session()
session_id = session_result.get('session_id')

# Add messages one by one
for msg in messages:
client.add_message(session_id, role=msg["role"], content=msg["text"])

# Commit session to trigger memory extraction
commit_result = client.commit_session(session_id)
task_id = commit_result.get("task_id")

# Wait for commit task to complete
if task_id:
now = time.time()
while True:
task = client.get_task(task_id)
if not task or task.get("status") in ("completed", "failed"):
break
time.sleep(1)
elapsed = time.time() - now
status = task.get("status", "unknown") if task else "not found"

client.close()


# ---------------------------------------------------------------------------
# Main import logic
# ---------------------------------------------------------------------------


def parse_session_range(s: str) -> tuple[int, int]:
"""Parse '1-4' or '3' into (lo, hi) inclusive tuple."""
if "-" in s:
Expand Down Expand Up @@ -243,7 +262,7 @@ def run_import(args: argparse.Namespace) -> None:

for sess in sessions:
meta = sess["meta"]
msg = sess["message"]
messages = sess["messages"]
label = f"{meta['session_key']} ({meta['date_time']})"

# Skip already ingested sessions unless force-ingest is enabled
Expand All @@ -265,11 +284,12 @@ def run_import(args: argparse.Namespace) -> None:
jsonl_output.flush()
continue

preview = msg.replace("\n", " | ")[:80]
print(f" [{label}] {preview}...", file=sys.stderr)
# Preview messages
preview = " | ".join([f"{msg['role']}: {msg['text'][:30]}..." for msg in messages[:3]])
print(f" [{label}] {preview}", file=sys.stderr)

try:
viking_ingest(msg)
viking_ingest(messages)
print(f" -> [SUCCESS] imported to OpenViking", file=sys.stderr)
success_count += 1

Expand Down Expand Up @@ -338,12 +358,21 @@ def run_import(args: argparse.Namespace) -> None:
jsonl_output.flush()
continue

combined_msg = "\n\n".join(session["messages"])
preview = combined_msg.replace("\n", " | ")[:80]
print(f" {preview}...", file=sys.stderr)
# For plain text, all messages as user role
messages = []
for i, text in enumerate(session["messages"]):
messages.append({
"role": "user",
"text": text.strip(),
"speaker": "user",
"index": i
})

preview = " | ".join([f"{msg['role']}: {msg['text'][:30]}..." for msg in messages[:3]])
print(f" {preview}", file=sys.stderr)

try:
viking_ingest(combined_msg)
viking_ingest(messages)
print(f" -> [SUCCESS] imported to OpenViking", file=sys.stderr)
success_count += 1

Expand Down Expand Up @@ -400,6 +429,7 @@ def run_import(args: argparse.Namespace) -> None:
# CLI
# ---------------------------------------------------------------------------


def main():
parser = argparse.ArgumentParser(description="Import conversations into OpenViking")
parser.add_argument(
Expand Down
44 changes: 44 additions & 0 deletions bot/scripts/kill_openviking_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash

# Kill OpenViking Server and vikingbot processes
# Usage: ./kill_openviking_server.sh

set -e

echo "=========================================="
echo "Stopping OpenViking processes"
echo "=========================================="

# Kill existing vikingbot processes
echo ""
echo "Step 1: Stopping vikingbot processes..."
if pgrep -f "vikingbot.*openapi" > /dev/null 2>&1 || pgrep -f "vikingbot.*gateway" > /dev/null 2>&1; then
pkill -f "vikingbot.*openapi" 2>/dev/null || true
pkill -f "vikingbot.*gateway" 2>/dev/null || true
sleep 2
echo " ✓ Stopped vikingbot processes"
else
echo " ✓ No vikingbot processes found"
fi

# Kill existing openviking-server processes
echo ""
echo "Step 2: Stopping openviking-server processes..."
if pgrep -f "openviking-server" > /dev/null 2>&1; then
pkill -f "openviking-server" 2>/dev/null || true
sleep 2
# Force kill if still running
if pgrep -f "openviking-server" > /dev/null 2>&1; then
echo " Force killing remaining processes..."
pkill -9 -f "openviking-server" 2>/dev/null || true
sleep 1
fi
echo " ✓ Stopped openviking-server processes"
else
echo " ✓ No openviking-server processes found"
fi

echo ""
echo "=========================================="
echo "✓ All processes stopped"
echo "=========================================="
24 changes: 14 additions & 10 deletions bot/scripts/test_restart_openviking_server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
set -e

# Default values
PORT="1933"
PORT="1934"
BOT_URL="http://localhost:18790"
TEST_CONFIG="$HOME/.openviking_test/ov.conf"
TEST_DATA_DIR="$HOME/.openviking_test/data"
Expand Down Expand Up @@ -67,21 +67,25 @@ else
echo " ✓ No existing vikingbot processes found"
fi

# Step 2: Kill existing openviking-server processes
# Step 2: Kill existing openviking-server on specific port
echo ""
echo "Step 2: Stopping existing openviking-server processes..."
if pgrep -f "openviking-server" > /dev/null 2>&1; then
pkill -f "openviking-server" 2>/dev/null || true
echo "Step 2: Stopping openviking-server on port $PORT..."
PID=$(lsof -ti :$PORT 2>/dev/null || true)
if [ -n "$PID" ]; then
echo " Found PID: $PID"
pkill -f "vikingbot.*openapi" 2>/dev/null || true
pkill -f "vikingbot.*gateway" 2>/dev/null || true
kill $PID 2>/dev/null || true
sleep 2
# Force kill if still running
if pgrep -f "openviking-server" > /dev/null 2>&1; then
echo " Force killing remaining processes..."
pkill -9 -f "openviking-server" 2>/dev/null || true
if lsof -ti :$PORT > /dev/null 2>&1; then
echo " Force killing..."
kill -9 $PID 2>/dev/null || true
sleep 1
fi
echo " ✓ Stopped existing processes"
echo " ✓ Stopped process on port $PORT"
else
echo " ✓ No existing processes found"
echo " ✓ No process found on port $PORT"
fi

# Step 3: Wait for port to be released
Expand Down
1 change: 1 addition & 0 deletions openviking/models/vlm/backends/openai_vlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ async def get_completion_async(
else:
kwargs_messages = [{"role": "user", "content": prompt}]


kwargs = {
"model": self.model or "gpt-4o-mini",
"messages": kwargs_messages,
Expand Down
Loading
Loading