Skill Tracer
Skills › Scientific Agent Skills › open-notebook › chat_interaction.py

chat_interaction.py

93 lines · skills/open-notebook/scripts/chat_interaction.py at 5f2bc03

1"""Context-aware chat and search helpers for Open Notebook v1.14.0.
2
3Requires requests. CLI lists a notebook's chat sessions; AI calls are explicit
4Python function calls. Set OPEN_NOTEBOOK_URL and optional OPEN_NOTEBOOK_PASSWORD.
5"""
6
7import argparse
8import json
9
10from _common import record_path, request_json
13def create_chat_session(notebook_id, title, model_override=None):
14 payload = {"notebook_id": notebook_id, "title": title}
15 if model_override:
16 payload["model_override"] = model_override
17 return request_json("POST", "/chat/sessions", json=payload)
20def list_chat_sessions(notebook_id):
21 return request_json("GET", "/chat/sessions", params={"notebook_id": notebook_id})
24def build_context(notebook_id, source_ids=None, note_ids=None):
25 """Build explicit full-text context from selected IDs.
27 None/None requests upstream's default short context for all notebook items.
28 Empty lists select no items. Check returned IDs/content: upstream can skip
29 unavailable records without failing the whole request.
30 """
31 config = {}
32 if source_ids is not None or note_ids is not None:
33 config = {
34 "sources": {sid: "full content" for sid in source_ids or []},
35 "notes": {nid: "full content" for nid in note_ids or []},
36 }
37 return request_json("POST", "/chat/context", json={
38 "notebook_id": notebook_id, "context_config": config,
39 })
42def send_chat_message(session_id, message, context, model_override=None):
43 """Send the built context dict (not the wrapper and not include_* flags)."""
44 if not isinstance(context, dict) or not {"sources", "notes"}.issubset(context):
45 raise ValueError("Pass build_context(...)[\"context\"] with sources and notes")
46 payload = {"session_id": session_id, "message": message, "context": context}
47 if model_override:
48 payload["model_override"] = model_override
49 result = request_json("POST", "/chat/execute", json=payload)
50 for msg in reversed(result["messages"]):
51 if msg["type"] == "ai":
52 print(msg["content"])
53 break
54 return result
57def get_session_history(session_id):
58 return request_json("GET", f"/chat/sessions/{record_path(session_id)}")
61def search_knowledge_base(query, search_type="vector", limit=5, minimum_score=0.2):
62 """Search globally on v1.14.0; unknown scope fields would be ignored there."""
63 if search_type not in {"text", "vector"}:
64 raise ValueError("search_type must be text or vector")
65 if not 1 <= limit <= 1000 or not 0 <= minimum_score <= 1:
66 raise ValueError("limit must be 1..1000 and minimum_score must be 0..1")
67 return request_json("POST", "/search", json={
68 "query": query, "type": search_type, "limit": limit,
69 "search_sources": True, "search_notes": True, "minimum_score": minimum_score,
70 })
73def ask_question(question, strategy_model, answer_model, final_answer_model):
74 """Use registered model record IDs for all three required Ask stages."""
75 return request_json("POST", "/search/ask/simple", json={
76 "question": question, "strategy_model": strategy_model,
77 "answer_model": answer_model, "final_answer_model": final_answer_model,
78 })
81def delete_chat_session(session_id):
82 return request_json("DELETE", f"/chat/sessions/{record_path(session_id)}")
85def main():
86 parser = argparse.ArgumentParser(description=__doc__)
87 parser.add_argument("notebook_id")
88 args = parser.parse_args()
89 print(json.dumps(list_chat_sessions(args.notebook_id), indent=2))
92if __name__ == "__main__":
93 main()