a
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user