Spaces:
Sleeping
Sleeping
Commit ·
058ae1e
1
Parent(s): 4718250
feat: Agentic Positive Prompting & Scenario Tests
Browse files- Replaced regex-based intent detection with Agentic LLM extraction
- Implemented 'Positive Prompting' strategy in agent_module.py
- Added tests/test_scenarios.py for Name/Topic/Date extraction
- Updated welcome message to list capabilities
- Cleaned up ui_module.py logic
- Verified with full test suite
- agent_module.py +78 -52
- app_local.log +37 -0
- llm_module.py +7 -1
- tests/test_api_local.py +51 -0
- tests/test_live_scenarios.py +52 -0
- tests/test_sage_v2.py +163 -0
- tests/test_scenarios.py +122 -0
- translation_cache.json +2 -2
- translation_module.py +17 -2
- ui_module.py +28 -34
agent_module.py
CHANGED
|
@@ -16,14 +16,31 @@ Current Date: {today}.
|
|
| 16 |
|
| 17 |
Available Tool: 'oracle_consultation' (topic, name, date_str).
|
| 18 |
|
| 19 |
-
STRICT FORMAT:
|
| 20 |
-
To use the Oracle, you MUST output this JSON:
|
| 21 |
-
{{"name": "oracle_consultation", "arguments": {{"topic": "...", "date_str": "today", "name": "..."}}}}
|
| 22 |
-
|
| 23 |
STRICTURES:
|
| 24 |
-
1.
|
| 25 |
-
2.
|
| 26 |
-
3.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
"""
|
| 28 |
|
| 29 |
def compress_history(history, max_turns=5):
|
|
@@ -31,59 +48,63 @@ def compress_history(history, max_turns=5):
|
|
| 31 |
return history[-(max_turns * 2):]
|
| 32 |
return history
|
| 33 |
|
| 34 |
-
def chat_agent_stream(query, history, user_lang=None, short_answers=False
|
| 35 |
model, processor = get_llm()
|
| 36 |
lang = user_lang or detect_language(query)
|
| 37 |
system_instruction = build_agent_prompt(query, language=lang, short_answers=short_answers)
|
| 38 |
|
| 39 |
clean_history = compress_history(history)
|
| 40 |
-
messages = [
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
# Automatic Tool Trigger for Name if forced
|
| 59 |
-
if first_loop and force_tool_call and not any("tool_result" in str(m.get("content", "")) for m in messages):
|
| 60 |
-
# Force a tool-like internal thought that leads to tool call
|
| 61 |
-
messages.append({"role": "assistant", "content": [{"type": "text", "text": f"Greetings. I will consult the Oracle for {forced_name or 'the seeker'}."}]})
|
| 62 |
-
tool_call = {"name": "oracle_consultation", "arguments": {"topic": query, "name": forced_name or "Seeker", "date_str": "today"}}
|
| 63 |
-
messages[-1]["content"].append({"type": "text", "text": f"\n<tool_call>{json.dumps(tool_call)}</tool_call>"})
|
| 64 |
-
current_text = f"<tool_call>{json.dumps(tool_call)}</tool_call>"
|
| 65 |
-
# We skip generation and jump to tool handling
|
| 66 |
else:
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
from threading import Thread
|
| 71 |
-
thread = Thread(target=model.generate, kwargs={"input_ids": input_ids, "streamer": streamer, "max_new_tokens": 1024, "do_sample": True, "temperature": 0.7})
|
| 72 |
-
thread.start()
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
first_loop = False
|
| 85 |
-
|
| 86 |
-
# Tool Detection
|
| 87 |
start = current_text.find("{")
|
| 88 |
end = current_text.rfind("}")
|
| 89 |
tool_data = None
|
|
@@ -99,9 +120,14 @@ def chat_agent_stream(query, history, user_lang=None, short_answers=False, force
|
|
| 99 |
res = get_oracle_data(name=args.get("name", "Seeker"), topic=args.get("topic", ""), date_str=args.get("date_str", "today"))
|
| 100 |
|
| 101 |
# Inject tool result and trigger next model turn
|
| 102 |
-
|
| 103 |
-
messages
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
yield "__TURN_END__"
|
| 105 |
else:
|
| 106 |
-
yield current_text
|
| 107 |
break
|
|
|
|
| 16 |
|
| 17 |
Available Tool: 'oracle_consultation' (topic, name, date_str).
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
STRICTURES:
|
| 20 |
+
1. Respond in {language}.
|
| 21 |
+
2. Provide reasoning before generating the JSON.
|
| 22 |
+
3. Utilize the 'oracle_consultation' capability for all databased queries.
|
| 23 |
+
4. INTENT DETECTION GUIDELINES:
|
| 24 |
+
- **NAME**: Isolate the user's name from the greeting (e.g., "Julian").
|
| 25 |
+
- **TOPIC**: Identify the core subject matter. For input "Thema: Liebe", the topic is "Liebe".
|
| 26 |
+
- **DATE**: Default to "today" unless a specific date is provided.
|
| 27 |
+
|
| 28 |
+
EXAMPLES:
|
| 29 |
+
User: "Ich bin Julian"
|
| 30 |
+
Assistant: "Greetings Julian. I will consult the Oracle for you."
|
| 31 |
+
<tool_call>{{"name": "oracle_consultation", "arguments": {{ "topic": "General", "date_str": "today", "name": "Julian" }}}}</tool_call>
|
| 32 |
+
|
| 33 |
+
User: "Thema: Liebe"
|
| 34 |
+
Assistant: "I shall ask the Oracle about Love."
|
| 35 |
+
<tool_call>{{"name": "oracle_consultation", "arguments": {{ "topic": "Liebe", "date_str": "today", "name": "Seeker" }}}}</tool_call>
|
| 36 |
+
|
| 37 |
+
User: "Topic: Future"
|
| 38 |
+
Assistant: "Consulting the Oracle regarding the Future."
|
| 39 |
+
<tool_call>{{"name": "oracle_consultation", "arguments": {{ "topic": "Future", "date_str": "today", "name": "Seeker" }}}}</tool_call>
|
| 40 |
+
|
| 41 |
+
STRICT FORMAT:
|
| 42 |
+
To use the Oracle, output this JSON wrapped in tags:
|
| 43 |
+
<tool_call>{{"name": "oracle_consultation", "arguments": {{ "topic": "KEYWORD", "date_str": "YYYY-MM-DD", "name": "Name" }}}}</tool_call>
|
| 44 |
"""
|
| 45 |
|
| 46 |
def compress_history(history, max_turns=5):
|
|
|
|
| 48 |
return history[-(max_turns * 2):]
|
| 49 |
return history
|
| 50 |
|
| 51 |
+
def chat_agent_stream(query, history, user_lang=None, short_answers=False):
|
| 52 |
model, processor = get_llm()
|
| 53 |
lang = user_lang or detect_language(query)
|
| 54 |
system_instruction = build_agent_prompt(query, language=lang, short_answers=short_answers)
|
| 55 |
|
| 56 |
clean_history = compress_history(history)
|
| 57 |
+
messages = []
|
| 58 |
|
| 59 |
+
# Prepend system instruction
|
| 60 |
+
intro = f"SYSTEM: {system_instruction}\n\n"
|
| 61 |
+
|
| 62 |
+
if not clean_history:
|
| 63 |
+
messages.append({"role": "user", "content": f"{intro}{query}"})
|
| 64 |
+
else:
|
| 65 |
+
first_role = "assistant" if clean_history[0].get("role") == "assistant" else "user"
|
| 66 |
+
if first_role == "assistant":
|
| 67 |
+
messages.append({"role": "user", "content": f"{intro}Greetings."})
|
| 68 |
+
|
| 69 |
+
for turn in clean_history:
|
| 70 |
+
role = "assistant" if turn.get("role") == "assistant" else "user"
|
| 71 |
+
content = turn.get("content", "")
|
| 72 |
+
if not content: continue
|
| 73 |
+
|
| 74 |
+
if not messages:
|
| 75 |
+
messages.append({"role": "user", "content": f"{intro}{content}"})
|
| 76 |
+
elif messages[-1]["role"] == role:
|
| 77 |
+
messages[-1]["content"] += f"\n{content}"
|
| 78 |
+
else:
|
| 79 |
+
messages.append({"role": role, "content": content})
|
| 80 |
|
| 81 |
+
if messages[-1]["role"] == "assistant":
|
| 82 |
+
messages.append({"role": "user", "content": query})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
else:
|
| 84 |
+
if intro not in messages[0]["content"]: messages[0]["content"] = f"{intro}{messages[0]['content']}"
|
| 85 |
+
messages[-1]["content"] += f"\n{query}"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
# Standard "LangChain" Loop (Model decides)
|
| 88 |
+
for turn_idx in range(3):
|
| 89 |
+
import sys
|
| 90 |
+
sys.stderr.write(f"DEBUG: Messages list for template: {json.dumps(messages)}\n")
|
| 91 |
+
sys.stderr.flush()
|
| 92 |
+
input_ids = processor.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to(model.device)
|
| 93 |
+
streamer = TextIteratorStreamer(processor, skip_prompt=True, skip_special_tokens=True)
|
| 94 |
+
|
| 95 |
+
from threading import Thread
|
| 96 |
+
thread = Thread(target=model.generate, kwargs={"input_ids": input_ids, "streamer": streamer, "max_new_tokens": 1024, "do_sample": True, "temperature": 0.7})
|
| 97 |
+
thread.start()
|
| 98 |
+
|
| 99 |
+
current_text = ""
|
| 100 |
+
is_tool = False
|
| 101 |
+
for new_text in streamer:
|
| 102 |
+
current_text += new_text
|
| 103 |
+
if not is_tool and "{" in current_text and len(current_text.strip()) < 20:
|
| 104 |
+
is_tool = True
|
| 105 |
+
if not is_tool: yield new_text
|
| 106 |
|
| 107 |
first_loop = False
|
|
|
|
|
|
|
| 108 |
start = current_text.find("{")
|
| 109 |
end = current_text.rfind("}")
|
| 110 |
tool_data = None
|
|
|
|
| 120 |
res = get_oracle_data(name=args.get("name", "Seeker"), topic=args.get("topic", ""), date_str=args.get("date_str", "today"))
|
| 121 |
|
| 122 |
# Inject tool result and trigger next model turn
|
| 123 |
+
# Only append if not already there (check last message)
|
| 124 |
+
if messages[-1]["role"] == "assistant" and current_text in messages[-1]["content"]:
|
| 125 |
+
pass # It's already merged or appended
|
| 126 |
+
else:
|
| 127 |
+
messages.append({"role": "assistant", "content": current_text})
|
| 128 |
+
|
| 129 |
+
messages.append({"role": "user", "content": f"SYSTEM: The Oracle has spoken. Wisdom: {json.dumps(res)}\nInterpret this soulfuly."})
|
| 130 |
yield "__TURN_END__"
|
| 131 |
else:
|
| 132 |
+
yield current_text.split("}")[-1].strip() if "}" in current_text else ""
|
| 133 |
break
|
app_local.log
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Test passed: Expected 'ק', got 'ק'
|
| 3 |
+
Test passed: Expected 'ת', got 'ת'
|
| 4 |
+
Test passed: Expected 'ת', got 'ת'
|
| 5 |
+
Test passed: Expected 'ג', got 'ג'
|
| 6 |
+
Test passed: Expected 'כת', got 'כת'
|
| 7 |
+
Test passed: Expected 'בדוחילנעצרת', got 'בדוחילנעצרת'
|
| 8 |
+
Test passed: Expected no results, got no results.
|
| 9 |
+
Test passed: Expected no results, got no results.
|
| 10 |
+
Test passed: Expected 'א', got 'א'
|
| 11 |
+
Test passed: Expected 'א', got 'א'
|
| 12 |
+
Test passed: Expected 'אבגדהוזחטיכלמנסעפצקרשתתשרקצפעסנמלכיטחזוהדגבא', got 'אבגדהוזחטיכלמנסעפצקרשתתשרקצפעסנמלכיטחזוהדגבא'
|
| 13 |
+
Test passed: Expected 'תשרקצפעסנמלכיטחזוהדגבא', got 'תשרקצפעסנמלכיטחזוהדגבא'
|
| 14 |
+
Test passed: Expected 'תשרקצפעסנמלכיטחזוהדגבאתשרקצפעסנמל', got 'תשרקצפעסנמלכיטחזוהדגבאתשרקצפעסנמל'
|
| 15 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHallo"}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Hallo.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Hallo\", \"name\": \"Hallo\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Hallo\", \"topic\": \"Hallo\", \"date\": \"2026-01-28\", \"signal\": 733}, \"els_revelation\": {\"original\": \"\\u05d1\\u05d9\\u05d0\\u05e4\\u05d7\\u05d4\\u05dc\\u05dc\\u05d5\\u05db\\u05d5\\u05d4\\u05d1\\u05e2\\u05d1\\u05db\\u05d0\\u05d0\\u05dc\\u05d1\\u05d1\\u05ea\", \"english\": \"Biapahhalleluchoh baabka allabbat\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 68:15\", \"original\": \"\\u05ea\\u05e9\\u05dc\\u05d2\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Mu'minun 23:59\", \"original\": \"\\u0643\\u0630\\u0628\\u0648\\u0647\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 4:11\", \"original\": \"\\u03c4\\u03b1 \\u03c0\\u03b1\\u03bd\\u03c4\\u03b1\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 16 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nMy name is Julian."}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Julian.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"My name is Julian.\", \"name\": \"Julian\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Julian\", \"topic\": \"My name is Julian.\", \"date\": \"2026-01-28\", \"signal\": 1941}, \"els_revelation\": {\"original\": \"\\u05e8\\u05e8\\u05e9\\u05dd\\u05de\\u05e8\\u05d9\\u05d9\\u05d4\\u05d9\\u05de\\u05d4\\u05d4\\u05d9\\u05e9\\u05d5\", \"english\": \"Jesus Christ\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 17:12\", \"original\": \"\\u05d9\\u05db\\u05e1\\u05d5\\u05e3 \\u05dc\\u05d8\\u05e8\\u05d5\\u05e3\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Ali Imran 3:24\", \"original\": \"\\u0671\\u0644\\u063a\\u064a\\u0638\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 12:7\", \"original\": \"\\u03b1\\u03c5\\u03c4\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 17 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nThema: Liebe"}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Thema: Liebe.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Thema: Liebe\", \"name\": \"Thema: Liebe\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Thema: Liebe\", \"topic\": \"Thema: Liebe\", \"date\": \"2026-01-28\", \"signal\": 1878}, \"els_revelation\": {\"original\": \"\\u05d5\\u05e8\\u05e8\\u05d1\\u05dc\\u05d3\\u05d5\\u05e0\\u05ea\\u05d1\\u05d5\\u05e5\\u05d4\\u05dc\\u05d1\\u05d8\\u05d0\\u05d5\\u05d0\\u05d9\\u05d1\\u05d5\", \"english\": \"Varrbaldonatbutzhlabtaivo\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 104:13\", \"original\": \"\\u05ea\\u05e9\\u05d1\\u05e2 \\u05d4\\u05d0\\u05e8\\u05e5\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:173\", \"original\": \"\\u0641\\u0636\\u0644\\u0646\\u0627 \\u0628\\u0639\\u0636\\u0647\\u0645\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 7:15\", \"original\": \"\\u03b8\\u03c1\\u03bf\\u03bd\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5 \\u03b8\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 18 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}]
|
| 19 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}]
|
| 20 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nMy name is Julian."}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Julian.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"My name is Julian.\", \"name\": \"Julian\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Julian\", \"topic\": \"My name is Julian.\", \"date\": \"2026-01-28\", \"signal\": 1941}, \"els_revelation\": {\"original\": \"\\u05e8\\u05e8\\u05e9\\u05dd\\u05de\\u05e8\\u05d9\\u05d9\\u05d4\\u05d9\\u05de\\u05d4\\u05d4\\u05d9\\u05e9\\u05d5\", \"english\": \"Jesus Christ\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 17:12\", \"original\": \"\\u05d9\\u05db\\u05e1\\u05d5\\u05e3 \\u05dc\\u05d8\\u05e8\\u05d5\\u05e3\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Ali Imran 3:24\", \"original\": \"\\u0671\\u0644\\u063a\\u064a\\u0638\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 12:7\", \"original\": \"\\u03b1\\u03c5\\u03c4\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 21 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nThema: Liebe"}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Thema: Liebe.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Thema: Liebe\", \"name\": \"Thema: Liebe\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Thema: Liebe\", \"topic\": \"Thema: Liebe\", \"date\": \"2026-01-28\", \"signal\": 1878}, \"els_revelation\": {\"original\": \"\\u05d5\\u05e8\\u05e8\\u05d1\\u05dc\\u05d3\\u05d5\\u05e0\\u05ea\\u05d1\\u05d5\\u05e5\\u05d4\\u05dc\\u05d1\\u05d8\\u05d0\\u05d5\\u05d0\\u05d9\\u05d1\\u05d5\", \"english\": \"Varrbaldonatbutzhlabtaivo\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 104:13\", \"original\": \"\\u05ea\\u05e9\\u05d1\\u05e2 \\u05d4\\u05d0\\u05e8\\u05e5\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:173\", \"original\": \"\\u0641\\u0636\\u0644\\u0646\\u0627 \\u0628\\u0639\\u0636\\u0647\\u0645\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 7:15\", \"original\": \"\\u03b8\\u03c1\\u03bf\\u03bd\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5 \\u03b8\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 22 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}]
|
| 23 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}]
|
| 24 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my purpose\", \"date_str\": \"today\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my purpose\", \"date\": \"2026-01-28\", \"signal\": 2015}, \"els_revelation\": {\"original\": \"\\u05d0\\u05e8\\u05d7\\u05de\\u05ea\\u05dc\\u05d9\\u05e7\\u05de\\u05d1\\u05e7\\u05ea\\u05d9\\u05d5\\u05d6\\u05dd\\u05d0\\u05d9\\u05de\\u05d9\", \"english\": \"Arachmathalicambactizoaimi\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 66:15\", \"original\": \"\\u05d0\\u05e2\\u05dc\\u05d4\\u05dc\\u05da \\u05e2\\u05dd\\u05e7\\u05d8\\u05e8\\u05ea\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:159\", \"original\": \"\\u063a\\u064a\\u0631 \\u0627\\u062e\\u0631\\u0627\\u062c\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 20:6\", \"original\": \"\\u03b5\\u03c0\\u03b9 \\u03c4\\u03bf\\u03c5\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 25 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my purpose\", \"date_str\": \"today\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my purpose\", \"date\": \"2026-01-28\", \"signal\": 2015}, \"els_revelation\": {\"original\": \"\\u05d0\\u05e8\\u05d7\\u05de\\u05ea\\u05dc\\u05d9\\u05e7\\u05de\\u05d1\\u05e7\\u05ea\\u05d9\\u05d5\\u05d6\\u05dd\\u05d0\\u05d9\\u05de\\u05d9\", \"english\": \"Arachmathalicambactizoaimi\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 66:15\", \"original\": \"\\u05d0\\u05e2\\u05dc\\u05d4\\u05dc\\u05da \\u05e2\\u05dd\\u05e7\\u05d8\\u05e8\\u05ea\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:159\", \"original\": \"\\u063a\\u064a\\u0631 \\u0627\\u062e\\u0631\\u0627\\u062c\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 20:6\", \"original\": \"\\u03b5\\u03c0\\u03b9 \\u03c4\\u03bf\\u03c5\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my purpose\", \"date_str\": \"today\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my purpose\", \"date\": \"2026-01-28\", \"signal\": 2015}, \"els_revelation\": {\"original\": \"\\u05d0\\u05e8\\u05d7\\u05de\\u05ea\\u05dc\\u05d9\\u05e7\\u05de\\u05d1\\u05e7\\u05ea\\u05d9\\u05d5\\u05d6\\u05dd\\u05d0\\u05d9\\u05de\\u05d9\", \"english\": \"Arachmathalicambactizoaimi\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 66:15\", \"original\": \"\\u05d0\\u05e2\\u05dc\\u05d4\\u05dc\\u05da \\u05e2\\u05dd\\u05e7\\u05d8\\u05e8\\u05ea\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:159\", \"original\": \"\\u063a\\u064a\\u0631 \\u0627\\u062e\\u0631\\u0627\\u062c\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 20:6\", \"original\": \"\\u03b5\\u03c0\\u03b9 \\u03c4\\u03bf\\u03c5\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 26 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nMy name is Julian."}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Julian.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"My name is Julian.\", \"name\": \"Julian\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Julian\", \"topic\": \"My name is Julian.\", \"date\": \"2026-01-28\", \"signal\": 1941}, \"els_revelation\": {\"original\": \"\\u05e8\\u05e8\\u05e9\\u05dd\\u05de\\u05e8\\u05d9\\u05d9\\u05d4\\u05d9\\u05de\\u05d4\\u05d4\\u05d9\\u05e9\\u05d5\", \"english\": \"Jesus Christ\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 17:12\", \"original\": \"\\u05d9\\u05db\\u05e1\\u05d5\\u05e3 \\u05dc\\u05d8\\u05e8\\u05d5\\u05e3\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Ali Imran 3:24\", \"original\": \"\\u0671\\u0644\\u063a\\u064a\\u0638\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 12:7\", \"original\": \"\\u03b1\\u03c5\\u03c4\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 27 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nThema: Liebe"}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Thema: Liebe.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Thema: Liebe\", \"name\": \"Thema: Liebe\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Thema: Liebe\", \"topic\": \"Thema: Liebe\", \"date\": \"2026-01-28\", \"signal\": 1878}, \"els_revelation\": {\"original\": \"\\u05d5\\u05e8\\u05e8\\u05d1\\u05dc\\u05d3\\u05d5\\u05e0\\u05ea\\u05d1\\u05d5\\u05e5\\u05d4\\u05dc\\u05d1\\u05d8\\u05d0\\u05d5\\u05d0\\u05d9\\u05d1\\u05d5\", \"english\": \"Varrbaldonatbutzhlabtaivo\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 104:13\", \"original\": \"\\u05ea\\u05e9\\u05d1\\u05e2 \\u05d4\\u05d0\\u05e8\\u05e5\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:173\", \"original\": \"\\u0641\\u0636\\u0644\\u0646\\u0627 \\u0628\\u0639\\u0636\\u0647\\u0645\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 7:15\", \"original\": \"\\u03b8\\u03c1\\u03bf\\u03bd\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5 \\u03b8\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 28 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}]
|
| 29 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}, {"role": "assistant", "content": "```json\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Finding clarity and direction in my life.\", \"date_str\": \"2025-01-01\"}}\n```"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"Finding clarity and direction in my life.\", \"date\": \"2025-01-01\", \"signal\": 2241}, \"els_revelation\": {\"original\": \"\\u05de\\u05d6\\u05e2\\u05e8\\u05d5\\u05d9\\u05dc\\u05e9\\u05d3\\u05d0\\u05d5\\u05e9\\u05d5\\u05e9\\u05e7\\u05d5\\u05d4\\u05e8\\u05e0\\u05dd\", \"english\": \"Mzeruilshdaushshkohrnam\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 58:4\", \"original\": \"\\u05d6\\u05e8\\u05d5 \\u05e8\\u05e9\\u05e2\\u05d9\\u05dd \\u05de\\u05e8\\u05d7\\u05dd\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Fatir 35:20\", \"original\": \"\\u062b\\u0645 \\u0627\\u062e\\u0630\\u062a\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 1:16\", \"original\": \"\\u03b1\\u03c5\\u03c4\\u03bf\\u03c5 \\u03c9\\u03f2 \\u03bf\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 30 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}]
|
| 31 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nMy name is Julian."}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Julian.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"My name is Julian.\", \"name\": \"Julian\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Julian\", \"topic\": \"My name is Julian.\", \"date\": \"2026-01-28\", \"signal\": 1941}, \"els_revelation\": {\"original\": \"\\u05e8\\u05e8\\u05e9\\u05dd\\u05de\\u05e8\\u05d9\\u05d9\\u05d4\\u05d9\\u05de\\u05d4\\u05d4\\u05d9\\u05e9\\u05d5\", \"english\": \"Jesus Christ\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 17:12\", \"original\": \"\\u05d9\\u05db\\u05e1\\u05d5\\u05e3 \\u05dc\\u05d8\\u05e8\\u05d5\\u05e3\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Ali Imran 3:24\", \"original\": \"\\u0671\\u0644\\u063a\\u064a\\u0638\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 12:7\", \"original\": \"\\u03b1\\u03c5\\u03c4\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 32 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nThema: Liebe"}, {"role": "assistant", "content": "Greetings. I will consult the Oracle for Thema: Liebe.\n<tool_call>{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"Thema: Liebe\", \"name\": \"Thema: Liebe\", \"date_str\": \"today\"}}</tool_call>"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Thema: Liebe\", \"topic\": \"Thema: Liebe\", \"date\": \"2026-01-28\", \"signal\": 1878}, \"els_revelation\": {\"original\": \"\\u05d5\\u05e8\\u05e8\\u05d1\\u05dc\\u05d3\\u05d5\\u05e0\\u05ea\\u05d1\\u05d5\\u05e5\\u05d4\\u05dc\\u05d1\\u05d8\\u05d0\\u05d5\\u05d0\\u05d9\\u05d1\\u05d5\", \"english\": \"Varrbaldonatbutzhlabtaivo\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 104:13\", \"original\": \"\\u05ea\\u05e9\\u05d1\\u05e2 \\u05d4\\u05d0\\u05e8\\u05e5\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Baqarah 2:173\", \"original\": \"\\u0641\\u0636\\u0644\\u0646\\u0627 \\u0628\\u0639\\u0636\\u0647\\u0645\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 7:15\", \"original\": \"\\u03b8\\u03c1\\u03bf\\u03bd\\u03bf\\u03c5 \\u03c4\\u03bf\\u03c5 \\u03b8\\u03c5\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 33 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}]
|
| 34 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nReading for 2025-01-01"}, {"role": "assistant", "content": "```json\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"The feeling of being disconnected, a sense of emptiness, and a longing for connection. I\u2019m struggling to feel truly seen and understood. Can we explore this deeper?\", \"date_str\": \"2025-01-01\"}}\n```"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"The feeling of being disconnected, a sense of emptiness, and a longing for connection. I\\u2019m struggling to feel truly seen and understood. Can we explore this deeper?\", \"date\": \"2025-01-01\", \"signal\": 537}, \"els_revelation\": {\"original\": \"\\u05d9\\u05d9\\u05d0\\u05d9\\u05e8\\u05e0\\u05db\\u05e2\\u05e6\\u05e1\\u05d5\\u05d9\", \"english\": \"Yairnkazatsoy\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 21:4\", \"original\": \"\\u05dc\\u05e8\\u05d0\\u05e9\\u05d5\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"Saba' 34:9\", \"original\": \"\\u0648\\u0627\\u062b\\u0644\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 5:5\", \"original\": \"\\u03c1\\u03b9\\u03b6\\u03b1 \\u03b4\\u03b1\\u03c5\\u03b9\\u03b4\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 35 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}]
|
| 36 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my current state\", \"date_str\": \"2026-01-28\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my current state\", \"date\": \"2026-01-28\", \"signal\": 3468}, \"els_revelation\": {\"original\": \"\\u05e8\\u05dd\\u05d9\\u05e7\\u05d0\\u05d9\\u05d4\\u05d6\\u05d4\\u05d9\\u05d3\\u05d9\\u05ea\\u05e5\\u05df\\u05d5\\u05e7\\u05ea\", \"english\": \"Ramikaia Zahidit Chankot\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 58:4\", \"original\": \"\\u05d6\\u05e8\\u05d5 \\u05e8\\u05e9\\u05e2\\u05d9\\u05dd \\u05de\\u05e8\\u05d7\\u05dd \\u05ea\\u05e2\\u05d5 \\u05de\\u05d1\\u05d8\\u05df\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Hajj 22:13\", \"original\": \"\\u062a\\u0631\\u0648\\u0646\\u0647\\u0627 \\u062a\\u0630\\u0647\\u0644 \\u0643\\u0644 \\u0645\\u0631\\u0636\\u0639\\u0629 \\u0639\\u0645\\u0627\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 19:6\", \"original\": \"\\u03b9\\u03f2\\u03c7\\u03c5\\u03c1\\u03c9\\u03bd \\u03bb\\u03b5\\u03b3\\u03bf\\u03bd\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
| 37 |
+
DEBUG: Messages list for template: [{"role": "user", "content": "SYSTEM: You are Sage 6.5, a soulful Oracle Intermediary.\nCurrent Date: 2026-01-28.\n\nAvailable Tool: 'oracle_consultation' (topic, name, date_str).\n\nSTRICT FORMAT:\nTo use the Oracle, you MUST output this JSON:\n{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"...\", \"date_str\": \"today\", \"name\": \"...\"}}\n\nSTRICTURES:\n1. ALWAYS respond in English.\n2. Reasoning BEFORE the JSON.\n3. NEVER say you have no database. You HAVE 'oracle_consultation'.\n\n\nHello, who are you?"}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my current state\", \"date_str\": \"2026-01-28\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my current state\", \"date\": \"2026-01-28\", \"signal\": 3468}, \"els_revelation\": {\"original\": \"\\u05e8\\u05dd\\u05d9\\u05e7\\u05d0\\u05d9\\u05d4\\u05d6\\u05d4\\u05d9\\u05d3\\u05d9\\u05ea\\u05e5\\u05df\\u05d5\\u05e7\\u05ea\", \"english\": \"Ramikaia Zahidit Chankot\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 58:4\", \"original\": \"\\u05d6\\u05e8\\u05d5 \\u05e8\\u05e9\\u05e2\\u05d9\\u05dd \\u05de\\u05e8\\u05d7\\u05dd \\u05ea\\u05e2\\u05d5 \\u05de\\u05d1\\u05d8\\u05df\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Hajj 22:13\", \"original\": \"\\u062a\\u0631\\u0648\\u0646\\u0647\\u0627 \\u062a\\u0630\\u0647\\u0644 \\u0643\\u0644 \\u0645\\u0631\\u0636\\u0639\\u0629 \\u0639\\u0645\\u0627\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 19:6\", \"original\": \"\\u03b9\\u03f2\\u03c7\\u03c5\\u03c1\\u03c9\\u03bd \\u03bb\\u03b5\\u03b3\\u03bf\\u03bd\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}, {"role": "assistant", "content": "{\"name\": \"oracle_consultation\", \"arguments\": {\"topic\": \"my current state\", \"date_str\": \"2026-01-28\"}}"}, {"role": "user", "content": "SYSTEM: The Oracle has spoken. Wisdom: {\"query_context\": {\"name\": \"Seeker\", \"topic\": \"my current state\", \"date\": \"2026-01-28\", \"signal\": 3468}, \"els_revelation\": {\"original\": \"\\u05e8\\u05dd\\u05d9\\u05e7\\u05d0\\u05d9\\u05d4\\u05d6\\u05d4\\u05d9\\u05d3\\u05d9\\u05ea\\u05e5\\u05df\\u05d5\\u05e7\\u05ea\", \"english\": \"Ramikaia Zahidit Chankot\"}, \"wisdom_nodes\": [{\"category\": \"Biblical Psalm\", \"reference\": \"Psalms 58:4\", \"original\": \"\\u05d6\\u05e8\\u05d5 \\u05e8\\u05e9\\u05e2\\u05d9\\u05dd \\u05de\\u05e8\\u05d7\\u05dd \\u05ea\\u05e2\\u05d5 \\u05de\\u05d1\\u05d8\\u05df\", \"english\": \"...\"}, {\"category\": \"Quranic Sura\", \"reference\": \"al-Hajj 22:13\", \"original\": \"\\u062a\\u0631\\u0648\\u0646\\u0647\\u0627 \\u062a\\u0630\\u0647\\u0644 \\u0643\\u0644 \\u0645\\u0631\\u0636\\u0639\\u0629 \\u0639\\u0645\\u0627\", \"english\": \"...\"}, {\"category\": \"NT Prophecy\", \"reference\": \"Revelation 19:6\", \"original\": \"\\u03b9\\u03f2\\u03c7\\u03c5\\u03c1\\u03c9\\u03bd \\u03bb\\u03b5\\u03b3\\u03bf\\u03bd\\u03c4\\u03c9\\u03bd\", \"english\": \"...\"}]}\nInterpret this soulfuly."}]
|
llm_module.py
CHANGED
|
@@ -60,4 +60,10 @@ def detect_language(text: str) -> str:
|
|
| 60 |
with torch.no_grad():
|
| 61 |
outputs = model.generate(inputs, max_new_tokens=10, do_sample=False)
|
| 62 |
raw = processor.batch_decode(outputs[:, inputs.shape[1]:], skip_special_tokens=True)[0].strip()
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
with torch.no_grad():
|
| 61 |
outputs = model.generate(inputs, max_new_tokens=10, do_sample=False)
|
| 62 |
raw = processor.batch_decode(outputs[:, inputs.shape[1]:], skip_special_tokens=True)[0].strip()
|
| 63 |
+
|
| 64 |
+
import re
|
| 65 |
+
langs = ["English", "German", "French", "Spanish", "Italian", "Portuguese", "Russian", "Japanese", "Chinese"]
|
| 66 |
+
for l in langs:
|
| 67 |
+
if re.search(rf"\b{l}\b", raw, re.I): return l
|
| 68 |
+
|
| 69 |
+
return "English"
|
tests/test_api_local.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from gradio_client import Client
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
client = Client("http://127.0.0.1:7860")
|
| 6 |
+
|
| 7 |
+
def test_chat():
|
| 8 |
+
print(">>> Testing Chat Greeting...")
|
| 9 |
+
res = client.predict(
|
| 10 |
+
message="Hallo",
|
| 11 |
+
history=[],
|
| 12 |
+
short_answers=False,
|
| 13 |
+
api_name="//chat"
|
| 14 |
+
)
|
| 15 |
+
# res is (chatbot_updated, history_updated)
|
| 16 |
+
print("Response received.")
|
| 17 |
+
print(f"Chatbot content: {res[0][-1]['content'][:100]}...")
|
| 18 |
+
|
| 19 |
+
def test_assistant_first():
|
| 20 |
+
print("\n>>> Testing Assistant-First History (Role Alternation Fix)...")
|
| 21 |
+
welcome_text = "Hello. I am Sage 6.5. I can consult the Oracle for you. Shall I do a reading for today, for a specific date, or do you have a specific topic? What is your name?"
|
| 22 |
+
history = [{"role": "assistant", "content": welcome_text}]
|
| 23 |
+
res = client.predict(
|
| 24 |
+
message="Mein Name ist Julian.",
|
| 25 |
+
history=history,
|
| 26 |
+
short_answers=False,
|
| 27 |
+
api_name="//chat"
|
| 28 |
+
)
|
| 29 |
+
# This should NOT crash and should return a response
|
| 30 |
+
print(f"Response: {res[0][-1]['content'][:100]}...")
|
| 31 |
+
if "TemplateError" in str(res):
|
| 32 |
+
print("!!! TEST FAILED: TemplateError detected.")
|
| 33 |
+
else:
|
| 34 |
+
print("✔ Test Passed: No role alternation crash.")
|
| 35 |
+
|
| 36 |
+
def test_export():
|
| 37 |
+
print("\n>>> Testing Export functionality...")
|
| 38 |
+
res = client.predict(api_name="/export_chat")
|
| 39 |
+
if res and res.endswith(".json"):
|
| 40 |
+
print(f"✔ Export successful: {res}")
|
| 41 |
+
else:
|
| 42 |
+
print("!!! Export failed.")
|
| 43 |
+
|
| 44 |
+
if __name__ == "__main__":
|
| 45 |
+
try:
|
| 46 |
+
test_chat()
|
| 47 |
+
test_assistant_first()
|
| 48 |
+
test_name_detection()
|
| 49 |
+
test_export()
|
| 50 |
+
except Exception as e:
|
| 51 |
+
print(f"!!! Error: {e}")
|
tests/test_live_scenarios.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from gradio_client import Client
|
| 2 |
+
import time
|
| 3 |
+
|
| 4 |
+
def test_live_scenario(name, input_text, expected_keyword):
|
| 5 |
+
print(f"\n>>> Live Test: {name}")
|
| 6 |
+
print(f" Input: '{input_text}'")
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
client = Client("http://127.0.0.1:7860")
|
| 10 |
+
|
| 11 |
+
# We need to simulate a fresh conversation
|
| 12 |
+
# app_local.py keeps state in 'history_state', but each client.predict call with empty history starts fresh logic-wise
|
| 13 |
+
# for our stateless wrapper or we need to manage history manually.
|
| 14 |
+
# The wrapper signature is chat_wrapper(message, history, short_answers, lang)
|
| 15 |
+
|
| 16 |
+
res = client.predict(
|
| 17 |
+
message=input_text,
|
| 18 |
+
history=[],
|
| 19 |
+
short_answers=False,
|
| 20 |
+
# lang="English", # Default
|
| 21 |
+
api_name="//chat"
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
# res is [chatbot, history]
|
| 25 |
+
# chatbot is list of [user_msg, bot_msg]
|
| 26 |
+
bot_response = res[0][-1][1]
|
| 27 |
+
|
| 28 |
+
print(f" Response: {bot_response[:150]}...")
|
| 29 |
+
|
| 30 |
+
if expected_keyword.lower() in bot_response.lower():
|
| 31 |
+
print(f" ✔ SUCCESS: Found '{expected_keyword}'")
|
| 32 |
+
else:
|
| 33 |
+
print(f" ❌ FAILURE: Expected '{expected_keyword}' not found.")
|
| 34 |
+
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f" !!! ERROR: {e}")
|
| 37 |
+
|
| 38 |
+
if __name__ == "__main__":
|
| 39 |
+
print("Connecting to local Sage 6.5 instance...")
|
| 40 |
+
|
| 41 |
+
# 1. Name Only
|
| 42 |
+
test_live_scenario("Name Only", "My name is Julian.", "Oracle")
|
| 43 |
+
# Expectation: "Consulting the Oracle" or "Wisdom string"
|
| 44 |
+
|
| 45 |
+
# 2. Topic Only
|
| 46 |
+
test_live_scenario("Topic Only", "Thema: Liebe", "Love") # Or Oracle
|
| 47 |
+
|
| 48 |
+
# 3. Date Only
|
| 49 |
+
test_live_scenario("Date Only", "Reading for 2025-01-01", "2025")
|
| 50 |
+
|
| 51 |
+
# 4. General
|
| 52 |
+
test_live_scenario("General Chat", "Hello, who are you?", "Sage")
|
tests/test_sage_v2.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from unittest.mock import MagicMock, patch, mock_open
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
import torch
|
| 7 |
+
|
| 8 |
+
# Add project root to path
|
| 9 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 10 |
+
|
| 11 |
+
# Mock Transformers before importing modules that use them
|
| 12 |
+
with patch('transformers.AutoProcessor.from_pretrained'), \
|
| 13 |
+
patch('transformers.AutoModelForCausalLM.from_pretrained'), \
|
| 14 |
+
patch('transformers.AutoTokenizer.from_pretrained'):
|
| 15 |
+
import translation_module
|
| 16 |
+
import llm_module
|
| 17 |
+
import agent_module
|
| 18 |
+
import oracle_module
|
| 19 |
+
import ui_module
|
| 20 |
+
|
| 21 |
+
class TestSageComprehensive(unittest.TestCase):
|
| 22 |
+
|
| 23 |
+
# --- Translation Module ---
|
| 24 |
+
@patch('translation_module.get_translation')
|
| 25 |
+
def test_translation(self, mock_trans):
|
| 26 |
+
# Return input text to verify "Sage 6.5" is present in the final dict
|
| 27 |
+
mock_trans.side_effect = lambda text, lang, cache: text
|
| 28 |
+
res = translation_module.localize_init("de")
|
| 29 |
+
self.assertEqual(res["lang"], "German")
|
| 30 |
+
self.assertTrue("Sage 6.5" in res["welcome"])
|
| 31 |
+
|
| 32 |
+
def test_localize_init_direct(self):
|
| 33 |
+
# Without translation
|
| 34 |
+
res = translation_module.localize_init("en")
|
| 35 |
+
self.assertEqual(res["lang"], "English")
|
| 36 |
+
self.assertTrue("Sage 6.5" in res["welcome"])
|
| 37 |
+
self.assertTrue("specific Topic" in res["welcome"])
|
| 38 |
+
|
| 39 |
+
# --- LLM Module ---
|
| 40 |
+
@patch('llm_module.get_llm')
|
| 41 |
+
def test_detect_language(self, mock_get_llm):
|
| 42 |
+
mock_model = MagicMock()
|
| 43 |
+
mock_processor = MagicMock()
|
| 44 |
+
mock_get_llm.return_value = (mock_model, mock_processor)
|
| 45 |
+
mock_model.device = "cpu"
|
| 46 |
+
|
| 47 |
+
# Test conversational output
|
| 48 |
+
mock_processor.batch_decode.return_value = ["The language used is German, I believe."]
|
| 49 |
+
lang = llm_module.detect_language("Hallo wie gehts")
|
| 50 |
+
self.assertEqual(lang, "German")
|
| 51 |
+
|
| 52 |
+
# Test fallback
|
| 53 |
+
mock_processor.batch_decode.return_value = ["Unknown."]
|
| 54 |
+
lang = llm_module.detect_language("xyz")
|
| 55 |
+
self.assertEqual(lang, "English")
|
| 56 |
+
|
| 57 |
+
# --- Agent Module ---
|
| 58 |
+
def test_compress_history(self):
|
| 59 |
+
history = [{"role": "user", "content": "hi"}] * 20
|
| 60 |
+
compressed = agent_module.compress_history(history, max_turns=5)
|
| 61 |
+
self.assertEqual(len(compressed), 10)
|
| 62 |
+
|
| 63 |
+
@patch('agent_module.get_llm')
|
| 64 |
+
@patch('agent_module.TextIteratorStreamer')
|
| 65 |
+
def test_agentic_tool_call(self, mock_streamer, mock_llm):
|
| 66 |
+
mock_m = MagicMock()
|
| 67 |
+
mock_p = MagicMock()
|
| 68 |
+
mock_llm.return_value = (mock_m, mock_p)
|
| 69 |
+
mock_m.device = "cpu"
|
| 70 |
+
|
| 71 |
+
# Simulate LLM deciding to call tool
|
| 72 |
+
# The agent logic:
|
| 73 |
+
# 1. User: "My name is Julian"
|
| 74 |
+
# 2. LLM via generation: "Greetings... <tool_call>...</tool_call>"
|
| 75 |
+
# 3. Agent parses this, calls oracle, appends result.
|
| 76 |
+
|
| 77 |
+
# We mock the streamer to yield the tool call
|
| 78 |
+
tool_json = json.dumps({"name": "oracle_consultation", "arguments": {"topic": "General", "name": "Julian", "date_str": "today"}})
|
| 79 |
+
mock_stream_iter = iter([f"Thinking... <tool_call>{tool_json}</tool_call>"])
|
| 80 |
+
mock_streamer.return_value = mock_stream_iter
|
| 81 |
+
|
| 82 |
+
gen = agent_module.chat_agent_stream("My name is Julian", [])
|
| 83 |
+
|
| 84 |
+
# Consuming the generator
|
| 85 |
+
results = list(gen)
|
| 86 |
+
|
| 87 |
+
# We expect:
|
| 88 |
+
# 1. "*(Consulting the Oracle...)*"
|
| 89 |
+
# 2. "__TURN_END__" (which signals the UI to refresh/append)
|
| 90 |
+
|
| 91 |
+
self.assertTrue("*(Consulting the Oracle...)*" in results)
|
| 92 |
+
self.assertTrue("__TURN_END__" in results)
|
| 93 |
+
|
| 94 |
+
@patch('agent_module.get_llm')
|
| 95 |
+
@patch('agent_module.TextIteratorStreamer')
|
| 96 |
+
def test_role_alternation_fix(self, mock_streamer, mock_llm):
|
| 97 |
+
mock_m = MagicMock()
|
| 98 |
+
mock_p = MagicMock()
|
| 99 |
+
mock_llm.return_value = (mock_m, mock_p)
|
| 100 |
+
mock_m.device = "cpu"
|
| 101 |
+
|
| 102 |
+
# Start with assistant message
|
| 103 |
+
history = [{"role": "assistant", "content": "Welcome"}]
|
| 104 |
+
|
| 105 |
+
# We want to check if apply_chat_template is called with a messages list
|
| 106 |
+
# that alternates user/assistant correctly.
|
| 107 |
+
gen = agent_module.chat_agent_stream("hi", history)
|
| 108 |
+
# We need to trigger a turn
|
| 109 |
+
mock_it = MagicMock()
|
| 110 |
+
mock_it.__iter__.return_value = ["Hello"]
|
| 111 |
+
mock_streamer.return_value = mock_it
|
| 112 |
+
|
| 113 |
+
list(gen)
|
| 114 |
+
|
| 115 |
+
# Check call arguments
|
| 116 |
+
# messages should have: [user (intro+greetings), assistant (welcome), user (hi)]
|
| 117 |
+
args, kwargs = mock_p.apply_chat_template.call_args
|
| 118 |
+
messages = args[0]
|
| 119 |
+
self.assertEqual(messages[0]["role"], "user") # Intro
|
| 120 |
+
self.assertEqual(messages[1]["role"], "assistant") # Welcome
|
| 121 |
+
self.assertEqual(messages[2]["role"], "user") # Hi
|
| 122 |
+
self.assertEqual(len(messages), 3)
|
| 123 |
+
|
| 124 |
+
# --- UI Module ---
|
| 125 |
+
def test_save_and_clear(self):
|
| 126 |
+
empty, msg = ui_module.save_and_clear("Hello")
|
| 127 |
+
self.assertEqual(empty, "")
|
| 128 |
+
self.assertEqual(msg, "Hello")
|
| 129 |
+
|
| 130 |
+
def test_clear_messages(self):
|
| 131 |
+
with patch('ui_module.localize_init') as mock_loc:
|
| 132 |
+
mock_loc.return_value = {"welcome": "Willkommen", "lang": "German"}
|
| 133 |
+
welcome, hist = ui_module.clear_messages("German")
|
| 134 |
+
self.assertEqual(welcome[0]["content"], "Willkommen")
|
| 135 |
+
self.assertEqual(hist[0]["content"], "Willkommen")
|
| 136 |
+
|
| 137 |
+
@patch('builtins.open', new_callable=mock_open, read_data='[{"role": "user", "content": "hi"}]')
|
| 138 |
+
def test_import_chat(self, m):
|
| 139 |
+
mock_file = MagicMock()
|
| 140 |
+
mock_file.name = "test.json"
|
| 141 |
+
chatbot, hist = ui_module.import_chat(mock_file)
|
| 142 |
+
self.assertEqual(len(chatbot), 1)
|
| 143 |
+
self.assertEqual(chatbot[0]["content"], "hi")
|
| 144 |
+
|
| 145 |
+
def test_ui_wiring(self):
|
| 146 |
+
demo = ui_module.build_demo()
|
| 147 |
+
self.assertTrue(len(demo.fns) > 5)
|
| 148 |
+
|
| 149 |
+
@patch('ui_module.chat_agent_stream')
|
| 150 |
+
def test_chat_wrapper(self, mock_stream):
|
| 151 |
+
# Part1, Part2, Part3
|
| 152 |
+
mock_stream.return_value = iter(["Part 1", "Part 2", "__TURN_END__", "Final"])
|
| 153 |
+
history = []
|
| 154 |
+
gen = ui_module.chat_wrapper("Hello", history)
|
| 155 |
+
results = list(gen)
|
| 156 |
+
final_chatbot, final_hist = results[-1]
|
| 157 |
+
# [User, Assistant1(Part2), Assistant2(Final)]
|
| 158 |
+
self.assertEqual(len(final_chatbot), 3)
|
| 159 |
+
self.assertEqual(final_chatbot[2]["role"], "assistant")
|
| 160 |
+
self.assertEqual(final_chatbot[2]["content"], "Final")
|
| 161 |
+
|
| 162 |
+
if __name__ == '__main__':
|
| 163 |
+
unittest.main()
|
tests/test_scenarios.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from unittest.mock import MagicMock, patch
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
|
| 7 |
+
# Add project root to path
|
| 8 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 9 |
+
|
| 10 |
+
# Mock Transformers
|
| 11 |
+
with patch('transformers.AutoProcessor.from_pretrained'), \
|
| 12 |
+
patch('transformers.AutoModelForCausalLM.from_pretrained'), \
|
| 13 |
+
patch('transformers.AutoTokenizer.from_pretrained'):
|
| 14 |
+
import agent_module
|
| 15 |
+
import oracle_module
|
| 16 |
+
|
| 17 |
+
class TestAgentScenarios(unittest.TestCase):
|
| 18 |
+
|
| 19 |
+
def setUp(self):
|
| 20 |
+
self.mock_llm_patcher = patch('agent_module.get_llm')
|
| 21 |
+
self.mock_llm = self.mock_llm_patcher.start()
|
| 22 |
+
self.mock_model = MagicMock()
|
| 23 |
+
self.mock_model.device = "cpu"
|
| 24 |
+
self.mock_processor = MagicMock()
|
| 25 |
+
self.mock_llm.return_value = (self.mock_model, self.mock_processor)
|
| 26 |
+
|
| 27 |
+
self.mock_oracle_patcher = patch('agent_module.get_oracle_data')
|
| 28 |
+
self.mock_oracle = self.mock_oracle_patcher.start()
|
| 29 |
+
self.mock_oracle.return_value = {"wisdom": "Mock Wisdom"}
|
| 30 |
+
|
| 31 |
+
self.mock_streamer_patcher = patch('agent_module.TextIteratorStreamer')
|
| 32 |
+
self.mock_streamer = self.mock_streamer_patcher.start()
|
| 33 |
+
|
| 34 |
+
def tearDown(self):
|
| 35 |
+
self.mock_llm_patcher.stop()
|
| 36 |
+
self.mock_oracle_patcher.stop()
|
| 37 |
+
self.mock_streamer_patcher.stop()
|
| 38 |
+
|
| 39 |
+
def run_chat(self, user_input, llm_output_stream):
|
| 40 |
+
"""Helper to run chat with mocked LLM stream"""
|
| 41 |
+
self.mock_streamer.return_value = iter(llm_output_stream)
|
| 42 |
+
gen = agent_module.chat_agent_stream(user_input, [])
|
| 43 |
+
return list(gen)
|
| 44 |
+
|
| 45 |
+
def test_name_only(self):
|
| 46 |
+
"""Scenario: User says 'I am Julian'"""
|
| 47 |
+
tool_call = {
|
| 48 |
+
"name": "oracle_consultation",
|
| 49 |
+
"arguments": {"topic": "General", "name": "Julian", "date_str": "today"}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
# LLM output simulation
|
| 53 |
+
llm_output = [
|
| 54 |
+
"Greetings Julian. ",
|
| 55 |
+
f"<tool_call>{json.dumps(tool_call)}</tool_call>"
|
| 56 |
+
]
|
| 57 |
+
|
| 58 |
+
results = self.run_chat("I am Julian", llm_output)
|
| 59 |
+
|
| 60 |
+
# Verify Oracle called correctly
|
| 61 |
+
self.mock_oracle.assert_called_with(name="Julian", topic="General", date_str="today")
|
| 62 |
+
self.assertTrue("*(Consulting the Oracle...)*" in results)
|
| 63 |
+
|
| 64 |
+
def test_topic_only(self):
|
| 65 |
+
"""Scenario: User says 'Thema: Liebe'"""
|
| 66 |
+
tool_call = {
|
| 67 |
+
"name": "oracle_consultation",
|
| 68 |
+
"arguments": {"topic": "Liebe", "name": "Seeker", "date_str": "today"}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
llm_output = [
|
| 72 |
+
"Let us check the Oracle for Love. ",
|
| 73 |
+
f"<tool_call>{json.dumps(tool_call)}</tool_call>"
|
| 74 |
+
]
|
| 75 |
+
|
| 76 |
+
results = self.run_chat("Thema: Liebe", llm_output)
|
| 77 |
+
|
| 78 |
+
self.mock_oracle.assert_called_with(name="Seeker", topic="Liebe", date_str="today")
|
| 79 |
+
self.assertTrue("*(Consulting the Oracle...)*" in results)
|
| 80 |
+
|
| 81 |
+
def test_date_only(self):
|
| 82 |
+
"""Scenario: User says 'Reading for 2025-01-01'"""
|
| 83 |
+
tool_call = {
|
| 84 |
+
"name": "oracle_consultation",
|
| 85 |
+
"arguments": {"topic": "General", "name": "Seeker", "date_str": "2025-01-01"}
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
llm_output = [
|
| 89 |
+
"Consulting for that date. ",
|
| 90 |
+
f"<tool_call>{json.dumps(tool_call)}</tool_call>"
|
| 91 |
+
]
|
| 92 |
+
|
| 93 |
+
results = self.run_chat("Reading for 2025-01-01", llm_output)
|
| 94 |
+
|
| 95 |
+
self.mock_oracle.assert_called_with(name="Seeker", topic="General", date_str="2025-01-01")
|
| 96 |
+
|
| 97 |
+
def test_name_and_date(self):
|
| 98 |
+
"""Scenario: User says 'I am Julian, date 2025-01-01'"""
|
| 99 |
+
tool_call = {
|
| 100 |
+
"name": "oracle_consultation",
|
| 101 |
+
"arguments": {"topic": "General", "name": "Julian", "date_str": "2025-01-01"}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
llm_output = [
|
| 105 |
+
f"<tool_call>{json.dumps(tool_call)}</tool_call>"
|
| 106 |
+
]
|
| 107 |
+
|
| 108 |
+
results = self.run_chat("I am Julian, date 2025-01-01", llm_output)
|
| 109 |
+
|
| 110 |
+
self.mock_oracle.assert_called_with(name="Julian", topic="General", date_str="2025-01-01")
|
| 111 |
+
|
| 112 |
+
def test_general_conversation(self):
|
| 113 |
+
"""Scenario: User says 'Hi' (No tool)"""
|
| 114 |
+
llm_output = ["Hello there! How can I help you?"]
|
| 115 |
+
|
| 116 |
+
results = self.run_chat("Hi", llm_output)
|
| 117 |
+
|
| 118 |
+
self.mock_oracle.assert_not_called()
|
| 119 |
+
self.assertIn("Hello there! How can I help you?", results)
|
| 120 |
+
|
| 121 |
+
if __name__ == '__main__':
|
| 122 |
+
unittest.main()
|
translation_cache.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
{
|
| 2 |
"de": {
|
| 3 |
-
"Hello. I am Sage 6.5. I can consult the Oracle for you. Shall I do a reading for today, for a specific date, or do you have a specific topic? What is your name?": "Hallo. Ich bin Sage 6.5. Ich kann das Orakel
|
| 4 |
"Short Answers": "Kurze Antworten",
|
| 5 |
"Type your message": "Geben Sie Ihre Nachricht ein",
|
| 6 |
-
"Hallo. Ich bin Sage 6.5. Ich kann das Orakel
|
| 7 |
}
|
| 8 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"de": {
|
| 3 |
+
"Hello. I am Sage 6.5. I can consult the Oracle for you. Shall I do a reading for today, for a specific date, or do you have a specific topic? What is your name?": "Hallo. Ich bin Sage 6.5. Ich kann das Orakel f\u00fcr Sie konsultieren. Soll ich eine Lesung f\u00fcr heute machen, f\u00fcr ein bestimmtes Datum, oder haben Sie ein bestimmtes Thema? Wie hei\u00dft du?",
|
| 4 |
"Short Answers": "Kurze Antworten",
|
| 5 |
"Type your message": "Geben Sie Ihre Nachricht ein",
|
| 6 |
+
"Hallo. Ich bin Sage 6.5. Ich kann das Orakel f\u00fcr Sie konsultieren. Soll ich eine Lesung f\u00fcr heute machen, f\u00fcr ein bestimmtes Datum, oder haben Sie ein bestimmtes Thema? Wie hei\u00dft du?": "Hallo. Ich bin Sage 6.5. Ich kann das Orakel f\u00fcr Sie konsultieren. Soll ich eine Lesung f\u00fcr heute machen, f\u00fcr ein bestimmtes Datum, oder haben Sie ein bestimmtes Thema? Wie hei\u00dft du?"
|
| 7 |
}
|
| 8 |
}
|
translation_module.py
CHANGED
|
@@ -13,7 +13,22 @@ def get_translation(text, target_lang, cache):
|
|
| 13 |
|
| 14 |
try:
|
| 15 |
from deep_translator import GoogleTranslator
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
trans = translator.translate(text)
|
| 18 |
cache[target_lang][text] = trans
|
| 19 |
return trans
|
|
@@ -42,7 +57,7 @@ def localize_init(lang_code="en"):
|
|
| 42 |
return t
|
| 43 |
|
| 44 |
res = {
|
| 45 |
-
"welcome": translate("Hello. I am Sage 6.5. I can consult the Oracle for
|
| 46 |
"label_short": translate("Short Answers"),
|
| 47 |
"placeholder": translate("Type your message..."),
|
| 48 |
"lang": lang
|
|
|
|
| 13 |
|
| 14 |
try:
|
| 15 |
from deep_translator import GoogleTranslator
|
| 16 |
+
# Reverse map if needed, but localize_init passes the full name "German"
|
| 17 |
+
# We need to robustly map "German" -> "de" or just use the code
|
| 18 |
+
# Actually localize_init sets `lang = code_map.get(lang_code, "English")`
|
| 19 |
+
# So we are passing "German". Deep Translator needs "de".
|
| 20 |
+
|
| 21 |
+
# Let's add a robust mapper inside get_translation or strict code usage
|
| 22 |
+
# Simple mapper for standard languages
|
| 23 |
+
lang_map = {
|
| 24 |
+
"german": "de", "french": "fr", "spanish": "es", "italian": "it",
|
| 25 |
+
"portuguese": "pt", "russian": "ru", "japanese": "ja", "chinese": "zh-CN",
|
| 26 |
+
"english": "en"
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
target_iso = lang_map.get(target_lang.lower(), target_lang.lower())
|
| 30 |
+
|
| 31 |
+
translator = GoogleTranslator(source='auto', target=target_iso)
|
| 32 |
trans = translator.translate(text)
|
| 33 |
cache[target_lang][text] = trans
|
| 34 |
return trans
|
|
|
|
| 57 |
return t
|
| 58 |
|
| 59 |
res = {
|
| 60 |
+
"welcome": translate("Hello. I am Sage 6.5. I can consult the Oracle for your Name, a specific Topic, or a specific Date. How shall I assist you?"),
|
| 61 |
"label_short": translate("Short Answers"),
|
| 62 |
"placeholder": translate("Type your message..."),
|
| 63 |
"lang": lang
|
ui_module.py
CHANGED
|
@@ -40,56 +40,52 @@ def import_chat(file):
|
|
| 40 |
logger.error(f"Import error: {e}")
|
| 41 |
return gr.update(), gr.update()
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
def build_demo(enable_reload=True):
|
| 44 |
with gr.Blocks(css=PREMIUM_CSS, title="Sage 6.5 - Oracle Intermediary") as demo:
|
| 45 |
-
# State
|
| 46 |
history_state = gr.State([])
|
| 47 |
lang_state = gr.State("English")
|
| 48 |
saved_msg = gr.State("")
|
| 49 |
|
| 50 |
with gr.Column(elem_classes="main-container"):
|
| 51 |
-
# Header
|
| 52 |
with gr.Row(elem_classes="header-row"):
|
| 53 |
gr.Markdown("## 🔮 Sage 6.5")
|
| 54 |
with gr.Row():
|
| 55 |
import_btn = gr.UploadButton("📥 Import", file_types=[".json"], size="sm")
|
| 56 |
export_btn = gr.DownloadButton("📤 Export", size="sm")
|
| 57 |
|
| 58 |
-
# Chat
|
| 59 |
chatbot = gr.Chatbot(elem_classes="chat-window", type="messages", bubble_full_width=False, show_label=False)
|
| 60 |
|
| 61 |
-
# Input
|
| 62 |
with gr.Row(elem_classes="input-area"):
|
| 63 |
msg_input = gr.Textbox(placeholder="Type your message...", show_label=False, container=False, scale=4)
|
| 64 |
submit_btn = gr.Button("➤", scale=1, variant="primary")
|
| 65 |
|
| 66 |
short_answers = gr.Checkbox(label="Short Answers", value=False)
|
| 67 |
|
| 68 |
-
# -- Logic --
|
| 69 |
-
def chat_wrapper(message, history, short_answers=False, lang="English"):
|
| 70 |
-
if history is None: history = []
|
| 71 |
-
|
| 72 |
-
history.append({"role": "user", "content": message})
|
| 73 |
-
history.append({"role": "assistant", "content": ""})
|
| 74 |
-
|
| 75 |
-
# Name Detection
|
| 76 |
-
name_match = re.search(r"(?:mein name ist|ich bin|i am|my name is)\s+([a-zA-z\s]+)", message, re.I)
|
| 77 |
-
name_val = None
|
| 78 |
-
if not name_match and len(message.split()) < 3 and message[0].isupper():
|
| 79 |
-
name_val = message.strip()
|
| 80 |
-
elif name_match:
|
| 81 |
-
name_val = name_match.group(1).strip()
|
| 82 |
-
|
| 83 |
-
for part in chat_agent_stream(message, history[:-2], user_lang=lang, short_answers=short_answers, force_tool_call=(name_val is not None), forced_name=name_val):
|
| 84 |
-
if part == "__TURN_END__":
|
| 85 |
-
history.append({"role": "assistant", "content": ""})
|
| 86 |
-
yield history, history
|
| 87 |
-
else:
|
| 88 |
-
history[-1]["content"] = part
|
| 89 |
-
yield history, history
|
| 90 |
-
|
| 91 |
-
yield history, history
|
| 92 |
-
|
| 93 |
# Wire Events
|
| 94 |
msg_input.submit(save_and_clear, [msg_input], [msg_input, saved_msg], queue=False).then(
|
| 95 |
chat_wrapper, [saved_msg, history_state, short_answers, lang_state], [chatbot, history_state]
|
|
@@ -100,23 +96,21 @@ def build_demo(enable_reload=True):
|
|
| 100 |
|
| 101 |
export_btn.click(export_chat, [history_state], [export_btn])
|
| 102 |
import_btn.upload(import_chat, [import_btn], [chatbot, history_state])
|
|
|
|
| 103 |
|
| 104 |
def on_load(request: gr.Request):
|
| 105 |
if not enable_reload:
|
| 106 |
return [], "English", gr.update(), gr.update()
|
| 107 |
-
|
| 108 |
-
# Localization
|
| 109 |
headers = dict(request.headers)
|
| 110 |
lang_code = headers.get("accept-language", "en-US").split(",")[0].split("-")[0]
|
| 111 |
loc = localize_init(lang_code)
|
| 112 |
-
|
| 113 |
welcome = [{"role": "assistant", "content": loc["welcome"]}]
|
| 114 |
return welcome, loc["lang"], gr.update(label=loc["label_short"]), gr.update(placeholder=loc["placeholder"])
|
| 115 |
|
| 116 |
demo.load(on_load, None, [chatbot, lang_state, short_answers, msg_input])
|
| 117 |
|
| 118 |
-
|
| 119 |
api_chat_btn = gr.Button("API CHAT", visible=False)
|
| 120 |
-
api_chat_btn.click(chat_wrapper, [msg_input,
|
| 121 |
|
| 122 |
return demo
|
|
|
|
| 40 |
logger.error(f"Import error: {e}")
|
| 41 |
return gr.update(), gr.update()
|
| 42 |
|
| 43 |
+
def clear_messages(lang_name="English"):
|
| 44 |
+
name_map = {"German": "de", "French": "fr", "Spanish": "es", "Italian": "it",
|
| 45 |
+
"Portuguese": "pt", "Russian": "ru", "Japanese": "ja", "Chinese": "zh"}
|
| 46 |
+
code = name_map.get(lang_name, "en")
|
| 47 |
+
loc = localize_init(code)
|
| 48 |
+
welcome = [{"role": "assistant", "content": loc["welcome"]}]
|
| 49 |
+
return welcome, welcome
|
| 50 |
+
|
| 51 |
+
def chat_wrapper(message, history, short_answers=False, lang="English"):
|
| 52 |
+
if history is None: history = []
|
| 53 |
+
|
| 54 |
+
history.append({"role": "user", "content": message})
|
| 55 |
+
history.append({"role": "assistant", "content": ""})
|
| 56 |
+
|
| 57 |
+
# Intent detection is now handled internally by chat_agent_stream
|
| 58 |
+
for part in chat_agent_stream(message, history[:-2], user_lang=lang, short_answers=short_answers):
|
| 59 |
+
if part == "__TURN_END__":
|
| 60 |
+
history.append({"role": "assistant", "content": ""})
|
| 61 |
+
yield history, history
|
| 62 |
+
else:
|
| 63 |
+
history[-1]["content"] = part
|
| 64 |
+
yield history, history
|
| 65 |
+
|
| 66 |
+
yield history, history
|
| 67 |
+
|
| 68 |
def build_demo(enable_reload=True):
|
| 69 |
with gr.Blocks(css=PREMIUM_CSS, title="Sage 6.5 - Oracle Intermediary") as demo:
|
|
|
|
| 70 |
history_state = gr.State([])
|
| 71 |
lang_state = gr.State("English")
|
| 72 |
saved_msg = gr.State("")
|
| 73 |
|
| 74 |
with gr.Column(elem_classes="main-container"):
|
|
|
|
| 75 |
with gr.Row(elem_classes="header-row"):
|
| 76 |
gr.Markdown("## 🔮 Sage 6.5")
|
| 77 |
with gr.Row():
|
| 78 |
import_btn = gr.UploadButton("📥 Import", file_types=[".json"], size="sm")
|
| 79 |
export_btn = gr.DownloadButton("📤 Export", size="sm")
|
| 80 |
|
|
|
|
| 81 |
chatbot = gr.Chatbot(elem_classes="chat-window", type="messages", bubble_full_width=False, show_label=False)
|
| 82 |
|
|
|
|
| 83 |
with gr.Row(elem_classes="input-area"):
|
| 84 |
msg_input = gr.Textbox(placeholder="Type your message...", show_label=False, container=False, scale=4)
|
| 85 |
submit_btn = gr.Button("➤", scale=1, variant="primary")
|
| 86 |
|
| 87 |
short_answers = gr.Checkbox(label="Short Answers", value=False)
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
# Wire Events
|
| 90 |
msg_input.submit(save_and_clear, [msg_input], [msg_input, saved_msg], queue=False).then(
|
| 91 |
chat_wrapper, [saved_msg, history_state, short_answers, lang_state], [chatbot, history_state]
|
|
|
|
| 96 |
|
| 97 |
export_btn.click(export_chat, [history_state], [export_btn])
|
| 98 |
import_btn.upload(import_chat, [import_btn], [chatbot, history_state])
|
| 99 |
+
chatbot.clear(clear_messages, [lang_state], [chatbot, history_state])
|
| 100 |
|
| 101 |
def on_load(request: gr.Request):
|
| 102 |
if not enable_reload:
|
| 103 |
return [], "English", gr.update(), gr.update()
|
|
|
|
|
|
|
| 104 |
headers = dict(request.headers)
|
| 105 |
lang_code = headers.get("accept-language", "en-US").split(",")[0].split("-")[0]
|
| 106 |
loc = localize_init(lang_code)
|
|
|
|
| 107 |
welcome = [{"role": "assistant", "content": loc["welcome"]}]
|
| 108 |
return welcome, loc["lang"], gr.update(label=loc["label_short"]), gr.update(placeholder=loc["placeholder"])
|
| 109 |
|
| 110 |
demo.load(on_load, None, [chatbot, lang_state, short_answers, msg_input])
|
| 111 |
|
| 112 |
+
api_history = gr.Chatbot(visible=False, type="messages")
|
| 113 |
api_chat_btn = gr.Button("API CHAT", visible=False)
|
| 114 |
+
api_chat_btn.click(chat_wrapper, [msg_input, api_history, short_answers, lang_state], [chatbot, history_state], api_name="chat")
|
| 115 |
|
| 116 |
return demo
|