Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

256 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""
Context Hub (chub) 包装器
为nanobot提供更友好的chub接口
"""
import subprocess
import json
import sys
import os
from typing import List, Dict, Any, Optional
class ChubWrapper:
"""chub CLI的Python包装器"""
def __init__(self):
"""初始化chub包装器"""
self.chub_path = self._find_chub()
def _find_chub(self) -> str:
"""查找chub可执行文件路径"""
try:
result = subprocess.run(['which', 'chub'],
capture_output=True,
text=True,
check=True)
return result.stdout.strip()
except subprocess.CalledProcessError:
raise RuntimeError("chub未安装。请运行: npm install -g @aisuite/chub")
def _run_chub(self, args: List[str], capture_output: bool = True) -> str:
"""运行chub命令"""
cmd = [self.chub_path] + args
try:
result = subprocess.run(cmd,
capture_output=capture_output,
text=True,
check=True)
return result.stdout
except subprocess.CalledProcessError as e:
error_msg = f"chub命令失败: {' '.join(cmd)}\n"
if e.stderr:
error_msg += f"错误: {e.stderr}"
raise RuntimeError(error_msg)
def search(self, query: str, json_output: bool = True) -> List[Dict[str, Any]]:
"""
搜索文档
Args:
query: 搜索查询
json_output: 是否返回JSON格式
Returns:
搜索结果列表
"""
args = ['search', query]
if json_output:
args.append('--json')
output = self._run_chub(args)
if json_output:
try:
data = json.loads(output)
return data.get('results', [])
except json.JSONDecodeError:
# 如果不是JSON格式,返回原始输出
return [{'raw_output': output}]
else:
return [{'raw_output': output}]
def get_doc(self, doc_id: str, language: Optional[str] = None,
output_file: Optional[str] = None) -> str:
"""
获取文档内容
Args:
doc_id: 文档ID (如 'openai/chat')
language: 语言 (如 'py', 'js', 'ts')
output_file: 输出文件路径
Returns:
文档内容
"""
args = ['get', doc_id]
if language:
args.extend(['--lang', language])
if output_file:
args.extend(['-o', output_file])
return self._run_chub(args, capture_output=not output_file)
def annotate(self, doc_id: str, note: str) -> str:
"""
添加注解
Args:
doc_id: 文档ID
note: 注解内容
Returns:
执行结果
"""
args = ['annotate', doc_id, note]
return self._run_chub(args)
def list_annotations(self) -> str:
"""列出所有注解"""
args = ['annotate', '--list']
return self._run_chub(args)
def feedback(self, doc_id: str, rating: str, label: Optional[str] = None) -> str:
"""
提供反馈
Args:
doc_id: 文档ID
rating: 评分 ('up' 或 'down')
label: 标签 (如 'outdated', 'inaccurate')
Returns:
执行结果
"""
args = ['feedback', doc_id, rating]
if label:
args.extend(['--label', label])
return self._run_chub(args)
def search_and_get(self, query: str, language: Optional[str] = None) -> Dict[str, Any]:
"""
搜索并获取文档(一步完成)
Args:
query: 搜索查询
language: 语言
Returns:
包含搜索结果和文档内容的字典
"""
# 搜索
results = self.search(query)
if not results:
return {'error': f'未找到与"{query}"相关的文档'}
# 获取第一个结果
first_result = results[0]
doc_id = first_result.get('id')
if not doc_id:
return {'error': '搜索结果中没有找到有效的文档ID'}
# 获取文档
try:
content = self.get_doc(doc_id, language)
return {
'doc_id': doc_id,
'result': first_result,
'content': content[:1000] + '...' if len(content) > 1000 else content
}
except Exception as e:
return {
'doc_id': doc_id,
'result': first_result,
'error': f'获取文档失败: {str(e)}'
}
def main():
"""命令行接口"""
import argparse
parser = argparse.ArgumentParser(description='Context Hub (chub) 包装器')
subparsers = parser.add_subparsers(dest='command', help='命令')
# 搜索命令
search_parser = subparsers.add_parser('search', help='搜索文档')
search_parser.add_argument('query', help='搜索查询')
search_parser.add_argument('--json', action='store_true', help='JSON输出')
# 获取命令
get_parser = subparsers.add_parser('get', help='获取文档')
get_parser.add_argument('doc_id', help='文档ID')
get_parser.add_argument('--lang', help='语言 (py, js, ts)')
get_parser.add_argument('-o', '--output', help='输出文件')
# 注解命令
annotate_parser = subparsers.add_parser('annotate', help='添加注解')
annotate_parser.add_argument('doc_id', help='文档ID')
annotate_parser.add_argument('note', help='注解内容')
# 列出注解命令
subparsers.add_parser('list-annotations', help='列出所有注解')
# 反馈命令
feedback_parser = subparsers.add_parser('feedback', help='提供反馈')
feedback_parser.add_argument('doc_id', help='文档ID')
feedback_parser.add_argument('rating', choices=['up', 'down'], help='评分')
feedback_parser.add_argument('--label', help='标签')
# 搜索并获取命令
search_get_parser = subparsers.add_parser('search-get', help='搜索并获取文档')
search_get_parser.add_argument('query', help='搜索查询')
search_get_parser.add_argument('--lang', help='语言')
args = parser.parse_args()
wrapper = ChubWrapper()
try:
if args.command == 'search':
results = wrapper.search(args.query, args.json)
if args.json:
print(json.dumps(results, indent=2, ensure_ascii=False))
else:
for result in results:
print(f"ID: {result.get('id', 'N/A')}")
print(f"名称: {result.get('name', 'N/A')}")
print(f"描述: {result.get('description', 'N/A')[:100]}...")
print()
elif args.command == 'get':
content = wrapper.get_doc(args.doc_id, args.lang, args.output)
if not args.output:
print(content)
elif args.command == 'annotate':
result = wrapper.annotate(args.doc_id, args.note)
print(result)
elif args.command == 'list-annotations':
result = wrapper.list_annotations()
print(result)
elif args.command == 'feedback':
result = wrapper.feedback(args.doc_id, args.rating, args.label)
print(result)
elif args.command == 'search-get':
result = wrapper.search_and_get(args.query, args.lang)
print(json.dumps(result, indent=2, ensure_ascii=False))
else:
parser.print_help()
except Exception as e:
print(f"错误: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()