Files
story-skill/bridge/ask_novelist.py

127 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""
Bridge script: caller agent → novelist agent (via Gateway API)
Automatically copies the novel source text to the novelist's workspace so that
the novelist agent can read the actual body text locally, not just chapter titles.
Then sends the request via Gateway API synchronously.
Usage:
python ask_novelist.py "continuation request"
python ask_novelist.py --novel /path/to/novel.txt "continuation request"
cat content.txt | python ask_novelist.py
"""
import sys
import os
import shutil
import json
import requests
# Config — customize these for your setup
GATEWAY_URL = "http://127.0.0.1:18789/v1/chat/completions"
GATEWAY_TOKEN = "<your-gateway-token>"
NOVELIST_MODEL = "openclaw/novelist"
NOVELIST_WORKSPACE = os.environ.get("NOVELIST_WORKSPACE", "/home/program/novelist-workspace")
NOVEL_TARGET_NAME = "novel_source.txt"
def ensure_novel_in_workspace(novel_path: str = None) -> str | None:
"""Copy the novel file to the novelist's workspace for local reading."""
if not novel_path or not os.path.isfile(novel_path):
# Try common default paths
candidates = [
"./novel.txt",
os.path.expanduser("~/novel.txt"),
]
for c in candidates:
if os.path.isfile(c):
novel_path = c
break
else:
return None
target = os.path.join(NOVELIST_WORKSPACE, NOVEL_TARGET_NAME)
try:
if not os.path.isfile(target) or os.path.getsize(target) != os.path.getsize(novel_path):
shutil.copy2(novel_path, target)
print(f"[桥接] 已复制小说到 {target}", file=sys.stderr)
return target
except Exception as e:
print(f"[警告] 复制小说文件失败: {e}", file=sys.stderr)
return None
def ask(question: str, timeout: int = 180) -> str:
"""Synchronous Gateway API call to novelist."""
payload = {
"model": NOVELIST_MODEL,
"messages": [{"role": "user", "content": question}],
"stream": False,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {GATEWAY_TOKEN}",
}
try:
resp = requests.post(GATEWAY_URL, json=payload, headers=headers, timeout=timeout)
if resp.status_code == 200:
data = resp.json()
choices = data.get("choices", [])
if choices:
return choices[0].get("message", {}).get("content", "")
return "[novelist] 返回为空"
return f"[novelist] 请求失败({resp.status_code}): {resp.text[:200]}"
except requests.exceptions.Timeout:
return "[novelist] 超时,请稍后重试"
except requests.exceptions.ConnectionError:
return "[novelist] 连接失败,请检查 Gateway 服务状态"
except Exception as e:
return f"[novelist] 请求异常: {str(e)}"
def build_prompt(novel_path: str | None, user_question: str) -> str:
"""Build the prompt sent to novelist. Includes novel file path hint."""
if novel_path:
return (
f"小说原文已放在你的工作区,路径为:{novel_path}\n\n"
f"请先阅读原文正文(不要只看标题),完成要求。\n\n"
f"要求如下:\n{user_question}"
)
return user_question
def main():
novel_path = None
args = sys.argv[1:]
if args and args[0] == "--novel":
if len(args) >= 2:
novel_path = args[1]
question = " ".join(args[2:]) if len(args) > 2 else ""
else:
print("错误:--novel 需要提供文件路径", file=sys.stderr)
sys.exit(1)
elif args:
question = " ".join(args)
if not question:
question = sys.stdin.read().strip()
if not question:
print("错误:未提供请求内容", file=sys.stderr)
sys.exit(1)
copied_path = ensure_novel_in_workspace(novel_path)
if copied_path:
novel_file = os.path.join(NOVELIST_WORKSPACE, NOVEL_TARGET_NAME)
else:
novel_file = None
prompt = build_prompt(novel_file, question)
result = ask(prompt)
print(result)
if __name__ == "__main__":
main()