thinkai-framework 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thinkai/__init__.py +24 -0
- thinkai/agent/__init__.py +12 -0
- thinkai/agent/base.py +83 -0
- thinkai/agent/react.py +148 -0
- thinkai/agent/tool.py +115 -0
- thinkai/core/__init__.py +22 -0
- thinkai/core/client.py +397 -0
- thinkai/core/config.py +153 -0
- thinkai/core/models.py +182 -0
- thinkai/exceptions.py +102 -0
- thinkai/middleware/__init__.py +11 -0
- thinkai/middleware/base.py +63 -0
- thinkai/middleware/logging_middleware.py +34 -0
- thinkai/middleware/retry_middleware.py +48 -0
- thinkai/prompt/template.py +145 -0
- thinkai/providers/__init__.py +18 -0
- thinkai/providers/base.py +303 -0
- thinkai/providers/deepseek.py +109 -0
- thinkai/providers/ollama.py +151 -0
- thinkai/providers/openai.py +108 -0
- thinkai/providers/qwen.py +117 -0
- thinkai/providers/registry.py +56 -0
- thinkai/rag/__init__.py +14 -0
- thinkai/rag/chroma_store.py +68 -0
- thinkai/rag/document_loader.py +189 -0
- thinkai/rag/pipeline.py +201 -0
- thinkai/rag/text_splitter.py +148 -0
- thinkai/rag/vector_store.py +27 -0
- thinkai/session/__init__.py +12 -0
- thinkai/session/context.py +67 -0
- thinkai/session/manager.py +96 -0
- thinkai/session/memory.py +70 -0
- thinkai/session/storage.py +39 -0
- thinkai/streaming.py +36 -0
- thinkai_framework-0.1.0.dist-info/METADATA +382 -0
- thinkai_framework-0.1.0.dist-info/RECORD +40 -0
- thinkai_framework-0.1.0.dist-info/WHEEL +5 -0
- thinkai_framework-0.1.0.dist-info/entry_points.txt +2 -0
- thinkai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- thinkai_framework-0.1.0.dist-info/top_level.txt +1 -0
thinkai/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ThinkAi - Enterprise-grade AI Framework
|
|
3
|
+
基于FastAPI的企业级AI大模型集成框架
|
|
4
|
+
开箱即用,支持多模型,RAG,Agent等核心能力
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.0"
|
|
8
|
+
__author__ = "ThinkAi Team"
|
|
9
|
+
|
|
10
|
+
from thinkai.core.client import ThinkAI
|
|
11
|
+
from thinkai.core.config import Settings
|
|
12
|
+
from thinkai.session.manager import SessionManager
|
|
13
|
+
from thinkai.prompt.template import PromptTemplate
|
|
14
|
+
from thinkai.rag.pipeline import RAGPipeline
|
|
15
|
+
from thinkai.agent.base import Agent
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ThinkAI",
|
|
19
|
+
"Settings",
|
|
20
|
+
"SessionManager",
|
|
21
|
+
"PromptTemplate",
|
|
22
|
+
"RAGPipeline",
|
|
23
|
+
"Agent",
|
|
24
|
+
]
|
thinkai/agent/base.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Agent基类"""
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import List, Optional, Dict, Any
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from thinkai.core.client import ThinkAI
|
|
7
|
+
from thinkai.core.models import ChatMessage
|
|
8
|
+
from thinkai.agent.tool import Tool
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AgentConfig(BaseModel):
|
|
12
|
+
"""Agent配置"""
|
|
13
|
+
name: str = Field(default="agent", description="Agent名称")
|
|
14
|
+
max_iterations: int = Field(default=10, description="最大迭代次数")
|
|
15
|
+
max_execution_time: int = Field(default=300, description="最大执行时间(秒)")
|
|
16
|
+
verbose: bool = Field(default=False, description="是否输出详细日志")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Agent(ABC):
|
|
20
|
+
"""
|
|
21
|
+
Agent基类
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
name: str = "agent",
|
|
27
|
+
model: Optional[str] = None,
|
|
28
|
+
tools: Optional[List[Tool]] = None,
|
|
29
|
+
max_iterations: int = 10,
|
|
30
|
+
verbose: bool = False,
|
|
31
|
+
ai_client: Optional[ThinkAI] = None,
|
|
32
|
+
**kwargs,
|
|
33
|
+
):
|
|
34
|
+
self.name = name
|
|
35
|
+
self.model = model
|
|
36
|
+
self.tools = tools or []
|
|
37
|
+
self.max_iterations = max_iterations
|
|
38
|
+
self.verbose = verbose
|
|
39
|
+
self.ai_client = ai_client
|
|
40
|
+
self.extra = kwargs
|
|
41
|
+
|
|
42
|
+
# 注册工具
|
|
43
|
+
self._tool_registry: Dict[str, Tool] = {}
|
|
44
|
+
for tool in self.tools:
|
|
45
|
+
self._tool_registry[tool.name] = tool
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
async def run(self, task: str, **kwargs) -> str:
|
|
49
|
+
"""
|
|
50
|
+
运行Agent
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
task: 任务描述
|
|
54
|
+
**kwargs: 额外参数
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
执行结果
|
|
58
|
+
"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def add_tool(self, tool: Tool):
|
|
62
|
+
"""添加工具"""
|
|
63
|
+
self._tool_registry[tool.name] = tool
|
|
64
|
+
self.tools.append(tool)
|
|
65
|
+
|
|
66
|
+
def remove_tool(self, tool_name: str):
|
|
67
|
+
"""移除工具"""
|
|
68
|
+
if tool_name in self._tool_registry:
|
|
69
|
+
del self._tool_registry[tool_name]
|
|
70
|
+
self.tools = [t for t in self.tools if t.name != tool_name]
|
|
71
|
+
|
|
72
|
+
def get_tool(self, tool_name: str) -> Optional[Tool]:
|
|
73
|
+
"""获取工具"""
|
|
74
|
+
return self._tool_registry.get(tool_name)
|
|
75
|
+
|
|
76
|
+
def list_tools(self) -> List[Tool]:
|
|
77
|
+
"""列出所有工具"""
|
|
78
|
+
return list(self._tool_registry.values())
|
|
79
|
+
|
|
80
|
+
def _log(self, message: str):
|
|
81
|
+
"""日志输出"""
|
|
82
|
+
if self.verbose:
|
|
83
|
+
print(f"[{self.name}] {message}")
|
thinkai/agent/react.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""ReAct Agent - 推理+行动循环"""
|
|
2
|
+
from typing import Optional, List, Dict, Any
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from thinkai.core.client import ThinkAI
|
|
6
|
+
from thinkai.core.models import ChatMessage
|
|
7
|
+
from thinkai.agent.base import Agent
|
|
8
|
+
from thinkai.agent.tool import Tool
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ReActAgent(Agent):
|
|
12
|
+
"""
|
|
13
|
+
ReAct Agent - 推理(Reasoning)和行动(Acting)交替执行
|
|
14
|
+
|
|
15
|
+
工作流程:
|
|
16
|
+
1. 接收任务
|
|
17
|
+
2. 思考下一步行动
|
|
18
|
+
3. 调用工具(如果需要)
|
|
19
|
+
4. 观察结果
|
|
20
|
+
5. 重复2-4直到完成任务
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
name: str = "react-agent",
|
|
26
|
+
model: Optional[str] = None,
|
|
27
|
+
tools: Optional[List[Tool]] = None,
|
|
28
|
+
max_iterations: int = 10,
|
|
29
|
+
verbose: bool = False,
|
|
30
|
+
ai_client: Optional[ThinkAI] = None,
|
|
31
|
+
):
|
|
32
|
+
super().__init__(
|
|
33
|
+
name=name,
|
|
34
|
+
model=model,
|
|
35
|
+
tools=tools,
|
|
36
|
+
max_iterations=max_iterations,
|
|
37
|
+
verbose=verbose,
|
|
38
|
+
ai_client=ai_client,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async def run(self, task: str, **kwargs) -> str:
|
|
42
|
+
"""
|
|
43
|
+
运行ReAct Agent
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
task: 任务描述
|
|
47
|
+
**kwargs: 额外参数
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
最终结果
|
|
51
|
+
"""
|
|
52
|
+
if not self.ai_client:
|
|
53
|
+
raise ValueError("ai_client is required for ReActAgent")
|
|
54
|
+
|
|
55
|
+
# 构建工具描述
|
|
56
|
+
tools_desc = self._format_tools()
|
|
57
|
+
|
|
58
|
+
# 构建系统提示
|
|
59
|
+
system_prompt = f"""You are a helpful AI assistant that can use tools to complete tasks.
|
|
60
|
+
|
|
61
|
+
Available tools:
|
|
62
|
+
{tools_desc}
|
|
63
|
+
|
|
64
|
+
When you need to use a tool, respond with:
|
|
65
|
+
Thought: [your reasoning]
|
|
66
|
+
Action: [tool_name]
|
|
67
|
+
Action Input: [tool input as JSON]
|
|
68
|
+
|
|
69
|
+
After getting the tool result:
|
|
70
|
+
Observation: [tool result]
|
|
71
|
+
|
|
72
|
+
When you have enough information to answer:
|
|
73
|
+
Thought: [your reasoning]
|
|
74
|
+
Final Answer: [your final answer]
|
|
75
|
+
|
|
76
|
+
Remember to think step by step and use tools when needed."""
|
|
77
|
+
|
|
78
|
+
messages = [
|
|
79
|
+
ChatMessage.system(system_prompt),
|
|
80
|
+
ChatMessage.user(task),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
iteration = 0
|
|
84
|
+
|
|
85
|
+
while iteration < self.max_iterations:
|
|
86
|
+
iteration += 1
|
|
87
|
+
self._log(f"Iteration {iteration}")
|
|
88
|
+
|
|
89
|
+
# 获取LLM响应
|
|
90
|
+
response = await self.ai_client.chat(
|
|
91
|
+
messages=messages,
|
|
92
|
+
model=self.model,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
content = response.content or ""
|
|
96
|
+
messages.append(ChatMessage.assistant(content))
|
|
97
|
+
|
|
98
|
+
self._log(f"Response: {content}")
|
|
99
|
+
|
|
100
|
+
# 检查是否有Final Answer
|
|
101
|
+
if "Final Answer:" in content:
|
|
102
|
+
final_answer = content.split("Final Answer:")[-1].strip()
|
|
103
|
+
self._log(f"Final Answer: {final_answer}")
|
|
104
|
+
return final_answer
|
|
105
|
+
|
|
106
|
+
# 解析Action
|
|
107
|
+
action_match = re.search(r"Action:\s*(\w+)", content)
|
|
108
|
+
action_input_match = re.search(r"Action Input:\s*(.+)", content, re.DOTALL)
|
|
109
|
+
|
|
110
|
+
if action_match and action_input_match:
|
|
111
|
+
tool_name = action_match.group(1)
|
|
112
|
+
try:
|
|
113
|
+
tool_input = json.loads(action_input_match.group(1).strip())
|
|
114
|
+
except json.JSONDecodeError:
|
|
115
|
+
tool_input = {"input": action_input_match.group(1).strip()}
|
|
116
|
+
|
|
117
|
+
self._log(f"Calling tool: {tool_name} with {tool_input}")
|
|
118
|
+
|
|
119
|
+
# 执行工具
|
|
120
|
+
tool = self.get_tool(tool_name)
|
|
121
|
+
if not tool:
|
|
122
|
+
observation = f"Error: Tool '{tool_name}' not found"
|
|
123
|
+
else:
|
|
124
|
+
try:
|
|
125
|
+
result = await tool.execute(**tool_input)
|
|
126
|
+
observation = str(result)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
observation = f"Error: {str(e)}"
|
|
129
|
+
|
|
130
|
+
self._log(f"Observation: {observation}")
|
|
131
|
+
|
|
132
|
+
messages.append(ChatMessage.user(f"Observation: {observation}"))
|
|
133
|
+
else:
|
|
134
|
+
# 没有Action,直接返回
|
|
135
|
+
return content
|
|
136
|
+
|
|
137
|
+
return "Error: Max iterations reached"
|
|
138
|
+
|
|
139
|
+
def _format_tools(self) -> str:
|
|
140
|
+
"""格式化工具描述"""
|
|
141
|
+
if not self.tools:
|
|
142
|
+
return "No tools available"
|
|
143
|
+
|
|
144
|
+
tools_str = []
|
|
145
|
+
for tool in self.tools:
|
|
146
|
+
tools_str.append(f"- {tool.name}: {tool.description}")
|
|
147
|
+
|
|
148
|
+
return "\n".join(tools_str)
|
thinkai/agent/tool.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""工具定义"""
|
|
2
|
+
from typing import Callable, Optional, Dict, Any, List
|
|
3
|
+
import inspect
|
|
4
|
+
import json
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tool:
|
|
9
|
+
"""
|
|
10
|
+
工具类 - 封装可被Agent调用的函数
|
|
11
|
+
|
|
12
|
+
使用示例:
|
|
13
|
+
@tool
|
|
14
|
+
def search(query: str) -> str:
|
|
15
|
+
\"\"\"Search the web\"\"\"
|
|
16
|
+
return "results"
|
|
17
|
+
|
|
18
|
+
或手动创建:
|
|
19
|
+
tool = Tool(
|
|
20
|
+
name="calculator",
|
|
21
|
+
description="Calculate math expressions",
|
|
22
|
+
func=calculate,
|
|
23
|
+
parameters={...}
|
|
24
|
+
)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
name: str,
|
|
30
|
+
description: str,
|
|
31
|
+
func: Callable,
|
|
32
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
33
|
+
):
|
|
34
|
+
self.name = name
|
|
35
|
+
self.description = description
|
|
36
|
+
self.func = func
|
|
37
|
+
self.parameters = parameters or self._infer_parameters(func)
|
|
38
|
+
|
|
39
|
+
def _infer_parameters(self, func: Callable) -> Dict[str, Any]:
|
|
40
|
+
"""从函数签名推断参数schema"""
|
|
41
|
+
sig = inspect.signature(func)
|
|
42
|
+
properties = {}
|
|
43
|
+
required = []
|
|
44
|
+
|
|
45
|
+
for name, param in sig.parameters.items():
|
|
46
|
+
param_type = "string"
|
|
47
|
+
if param.annotation == int:
|
|
48
|
+
param_type = "integer"
|
|
49
|
+
elif param.annotation == float:
|
|
50
|
+
param_type = "number"
|
|
51
|
+
elif param.annotation == bool:
|
|
52
|
+
param_type = "boolean"
|
|
53
|
+
elif param.annotation == list:
|
|
54
|
+
param_type = "array"
|
|
55
|
+
|
|
56
|
+
properties[name] = {
|
|
57
|
+
"type": param_type,
|
|
58
|
+
"description": f"Parameter {name}",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if param.default == inspect.Parameter.empty:
|
|
62
|
+
required.append(name)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": properties,
|
|
67
|
+
"required": required,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async def execute(self, **kwargs) -> Any:
|
|
71
|
+
"""执行工具"""
|
|
72
|
+
if inspect.iscoroutinefunction(self.func):
|
|
73
|
+
return await self.func(**kwargs)
|
|
74
|
+
return self.func(**kwargs)
|
|
75
|
+
|
|
76
|
+
def to_openai_format(self) -> Dict[str, Any]:
|
|
77
|
+
"""转换为OpenAI工具格式"""
|
|
78
|
+
return {
|
|
79
|
+
"type": "function",
|
|
80
|
+
"function": {
|
|
81
|
+
"name": self.name,
|
|
82
|
+
"description": self.description,
|
|
83
|
+
"parameters": self.parameters,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def __repr__(self) -> str:
|
|
88
|
+
return f"Tool(name='{self.name}', description='{self.description}')"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def tool(name: Optional[str] = None, description: Optional[str] = None):
|
|
92
|
+
"""
|
|
93
|
+
工具装饰器
|
|
94
|
+
|
|
95
|
+
使用示例:
|
|
96
|
+
@tool
|
|
97
|
+
def search(query: str) -> str:
|
|
98
|
+
\"\"\"Search the web\"\"\"
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
@tool(name="calc", description="Calculate")
|
|
102
|
+
def calculate(expr: str) -> str:
|
|
103
|
+
...
|
|
104
|
+
"""
|
|
105
|
+
def decorator(func: Callable) -> Tool:
|
|
106
|
+
tool_name = name or func.__name__
|
|
107
|
+
tool_desc = description or (func.__doc__ or "").strip()
|
|
108
|
+
|
|
109
|
+
return Tool(
|
|
110
|
+
name=tool_name,
|
|
111
|
+
description=tool_desc,
|
|
112
|
+
func=func,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return decorator
|
thinkai/core/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""核心模块"""
|
|
2
|
+
from thinkai.core.config import Settings
|
|
3
|
+
from thinkai.core.client import ThinkAI
|
|
4
|
+
from thinkai.core.models import (
|
|
5
|
+
ChatMessage,
|
|
6
|
+
ChatRequest,
|
|
7
|
+
ChatResponse,
|
|
8
|
+
ChatChoice,
|
|
9
|
+
Usage,
|
|
10
|
+
MessageRole,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Settings",
|
|
15
|
+
"ThinkAI",
|
|
16
|
+
"ChatMessage",
|
|
17
|
+
"ChatRequest",
|
|
18
|
+
"ChatResponse",
|
|
19
|
+
"ChatChoice",
|
|
20
|
+
"Usage",
|
|
21
|
+
"MessageRole",
|
|
22
|
+
]
|