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
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
call_openviking,
compact_json,
ensure_client,
extract_message_text,
item_value,
iter_result_items,
stringify,
Expand Down Expand Up @@ -683,7 +684,10 @@ def _normalize_messages(messages: str | list[dict[str, Any]]) -> list[dict[str,
}
)
continue
content = str(message.get("content", ""))
raw_content = message.get("content", "")
content = (
extract_message_text(raw_content) if isinstance(raw_content, list) else str(raw_content)
)
if role == "tool":
normalized.append(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
pytest.importorskip("langgraph")
pytest.importorskip("langchain_openviking")

from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
from langchain_core.messages import (
AIMessage,
HumanMessage,
SystemMessage,
ToolMessage,
convert_to_openai_messages,
)
from langchain_core.runnables import RunnableLambda
from langgraph.store.base import PutOp
from openviking_sdk.errors import InvalidArgumentError
Expand Down Expand Up @@ -177,6 +183,77 @@ def test_create_openviking_tools_exposes_common_viking_primitives():
assert "VikingDB is internal vector/index storage" in health


@pytest.mark.parametrize("role", ["user", "assistant", "system", "tool"])
@pytest.mark.parametrize("text_format", ["string", "block"])
def test_viking_store_normalizes_langchain_text_blocks(role: str, text_format: str):
content = [
{"type": "text", "text": "Remember cobalt deployment."},
{"type": "text", "text": "中文确认"},
]
messages = {
"user": HumanMessage(content=content),
"assistant": AIMessage(content=content),
"system": SystemMessage(content=content),
"tool": ToolMessage(content=content, tool_call_id="call-normal", name="ordinary_tool"),
}
wire = convert_to_openai_messages([messages[role]], text_format=text_format)
client = InMemoryOpenVikingClient()
store = create_openviking_tools(client=client, tool_names=["viking_store"])[0]

result = json.loads(
store.invoke({"session_id": "text-blocks", "messages": wire, "commit": False})
)

assert result["messages_added"] == 1
stored = client.sessions["text-blocks"][0]
assert stored["role"] == ("assistant" if role in {"system", "tool"} else role)
part = stored["parts"][0]
assert (
part["tool_output" if role == "tool" else "text"] == "Remember cobalt deployment.\n中文确认"
)
if role == "tool":
assert part["tool_id"] == "call-normal"
assert part["tool_name"] == "ordinary_tool"
assert part["tool_status"] == "completed"


@pytest.mark.parametrize("content", [None, False, 0])
def test_viking_store_preserves_scalar_content(content: Any):
client = InMemoryOpenVikingClient()
store = create_openviking_tools(client=client, tool_names=["viking_store"])[0]

store.invoke(
{
"session_id": "scalar",
"messages": [{"role": "user", "content": content}],
"commit": False,
}
)

assert client.sessions["scalar"][0]["parts"] == [{"type": "text", "text": str(content)}]


@pytest.mark.parametrize("role", ["user", "system", "tool"])
def test_viking_store_preserves_native_parts(role: str):
client = InMemoryOpenVikingClient()
store = create_openviking_tools(client=client, tool_names=["viking_store"])[0]
parts = [{"type": "text", "text": "explicit parts control"}]

store.invoke(
{
"session_id": "native-parts",
"messages": [
{"role": role, "content": [{"type": "text", "text": "unused"}], "parts": parts}
],
"commit": False,
}
)

stored = client.sessions["native-parts"][0]
assert stored["role"] == ("assistant" if role in {"system", "tool"} else role)
assert stored["parts"] == parts


def test_create_openviking_tools_uses_peer_id_only_for_message_attribution():
client = InMemoryOpenVikingClient(
{"viking://~/memories/profile.md": "The peer likes LangGraph agents."}
Expand Down
Loading