neobot-chat 1.0.0a7__tar.gz
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.
- neobot_chat-1.0.0a7/PKG-INFO +11 -0
- neobot_chat-1.0.0a7/README.md +0 -0
- neobot_chat-1.0.0a7/pyproject.toml +21 -0
- neobot_chat-1.0.0a7/src/neobot_chat/__init__.py +72 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/__init__.py +6 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/constants.py +1 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/executor.py +53 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/graph.py +60 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/nodes.py +22 -0
- neobot_chat-1.0.0a7/src/neobot_chat/graph/types.py +8 -0
- neobot_chat-1.0.0a7/src/neobot_chat/models.py +161 -0
- neobot_chat-1.0.0a7/src/neobot_chat/providers/__init__.py +6 -0
- neobot_chat-1.0.0a7/src/neobot_chat/providers/anthropic.py +260 -0
- neobot_chat-1.0.0a7/src/neobot_chat/providers/base.py +93 -0
- neobot_chat-1.0.0a7/src/neobot_chat/providers/deepseek_offical.py +399 -0
- neobot_chat-1.0.0a7/src/neobot_chat/providers/openai.py +192 -0
- neobot_chat-1.0.0a7/src/neobot_chat/py.typed +0 -0
- neobot_chat-1.0.0a7/src/neobot_chat/runtime/__init__.py +4 -0
- neobot_chat-1.0.0a7/src/neobot_chat/runtime/agent.py +301 -0
- neobot_chat-1.0.0a7/src/neobot_chat/runtime/prompt.py +164 -0
- neobot_chat-1.0.0a7/src/neobot_chat/runtime/workflow.py +53 -0
- neobot_chat-1.0.0a7/src/neobot_chat/schema/__init__.py +55 -0
- neobot_chat-1.0.0a7/src/neobot_chat/schema/exceptions.py +21 -0
- neobot_chat-1.0.0a7/src/neobot_chat/schema/protocol.py +40 -0
- neobot_chat-1.0.0a7/src/neobot_chat/schema/types.py +92 -0
- neobot_chat-1.0.0a7/src/neobot_chat/skills/__init__.py +4 -0
- neobot_chat-1.0.0a7/src/neobot_chat/skills/inject.py +38 -0
- neobot_chat-1.0.0a7/src/neobot_chat/skills/registry.py +121 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/__init__.py +15 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/builtin.py +300 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/composite.py +45 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/registry.py +144 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/shell.py +85 -0
- neobot_chat-1.0.0a7/src/neobot_chat/tools/toolset.py +92 -0
- neobot_chat-1.0.0a7/src/neobot_chat/utils/__init__.py +4 -0
- neobot_chat-1.0.0a7/src/neobot_chat/utils/preprocessors.py +21 -0
- neobot_chat-1.0.0a7/src/neobot_chat/utils/tools.py +9 -0
- neobot_chat-1.0.0a7/src/neobot_chat/utils/xml.py +122 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: neobot-chat
|
|
3
|
+
Version: 1.0.0a7
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author: wsrsq, tangtian
|
|
6
|
+
Author-email: wsrsq <wsrsq001@163.com>, tangtian <a14b@126.com>
|
|
7
|
+
Requires-Dist: httpx>=0.27.0
|
|
8
|
+
Requires-Dist: neobot-contracts
|
|
9
|
+
Requires-Python: >=3.13
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "neobot-chat"
|
|
3
|
+
version = "1.0.0-alpha.7"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "wsrsq", email = "wsrsq001@163.com" },
|
|
8
|
+
{ name = "tangtian", email = "a14b@126.com" },
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"httpx>=0.27.0",
|
|
13
|
+
"neobot-contracts",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[tool.uv.sources]
|
|
17
|
+
neobot-contracts = { workspace = true }
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build>=0.9.27,<0.10.0"]
|
|
21
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from neobot_chat.runtime.agent import Agent
|
|
2
|
+
from neobot_chat.graph import END, CompiledGraph, StateGraph, skill_node
|
|
3
|
+
from neobot_chat.schema.protocol import (
|
|
4
|
+
AgentLike,
|
|
5
|
+
ChatService,
|
|
6
|
+
Runnable,
|
|
7
|
+
StatePreprocessor,
|
|
8
|
+
StreamableRunnable,
|
|
9
|
+
ToolExecutor,
|
|
10
|
+
)
|
|
11
|
+
from neobot_chat.skills import SkillRegistry, build_skill_preprocessor, inject_skills
|
|
12
|
+
from neobot_chat.tools import AgentRegistry, BuiltinTools, CompositeToolExecutor, Toolset, build_builtin_toolset
|
|
13
|
+
from neobot_chat.models import (
|
|
14
|
+
ModelPricing,
|
|
15
|
+
ModelRegistry,
|
|
16
|
+
ModelSettings,
|
|
17
|
+
RegisteredModel,
|
|
18
|
+
create_provider,
|
|
19
|
+
get_model_registry,
|
|
20
|
+
get_registered_model,
|
|
21
|
+
register_model,
|
|
22
|
+
)
|
|
23
|
+
from neobot_chat.schema.types import (
|
|
24
|
+
ChatChunk,
|
|
25
|
+
MessageContent,
|
|
26
|
+
OnEvent,
|
|
27
|
+
State,
|
|
28
|
+
ToolAccessPolicy,
|
|
29
|
+
ToolAccessRule,
|
|
30
|
+
ToolGuardContext,
|
|
31
|
+
)
|
|
32
|
+
from neobot_chat.utils import compose_preprocessors, parse_tool_args
|
|
33
|
+
from neobot_chat.runtime.workflow import Workflow
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"Agent",
|
|
37
|
+
"AgentRegistry",
|
|
38
|
+
"AgentLike",
|
|
39
|
+
"BuiltinTools",
|
|
40
|
+
"build_builtin_toolset",
|
|
41
|
+
"CompositeToolExecutor",
|
|
42
|
+
"ChatService",
|
|
43
|
+
"ChatChunk",
|
|
44
|
+
"MessageContent",
|
|
45
|
+
"ModelPricing",
|
|
46
|
+
"ModelRegistry",
|
|
47
|
+
"ModelSettings",
|
|
48
|
+
"RegisteredModel",
|
|
49
|
+
"CompiledGraph",
|
|
50
|
+
"compose_preprocessors",
|
|
51
|
+
"create_provider",
|
|
52
|
+
"END",
|
|
53
|
+
"build_skill_preprocessor",
|
|
54
|
+
"get_model_registry",
|
|
55
|
+
"get_registered_model",
|
|
56
|
+
"inject_skills",
|
|
57
|
+
"OnEvent",
|
|
58
|
+
"parse_tool_args",
|
|
59
|
+
"register_model",
|
|
60
|
+
"Runnable",
|
|
61
|
+
"StatePreprocessor",
|
|
62
|
+
"skill_node",
|
|
63
|
+
"SkillRegistry",
|
|
64
|
+
"State",
|
|
65
|
+
"StateGraph",
|
|
66
|
+
"ToolAccessPolicy",
|
|
67
|
+
"ToolAccessRule",
|
|
68
|
+
"ToolGuardContext",
|
|
69
|
+
"StreamableRunnable",
|
|
70
|
+
"ToolExecutor",
|
|
71
|
+
"Workflow",
|
|
72
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
END = "__end__"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from neobot_chat.schema.exceptions import GraphError
|
|
4
|
+
from neobot_chat.graph.constants import END
|
|
5
|
+
from neobot_chat.graph.types import StateCondition, StateNode
|
|
6
|
+
from neobot_chat.schema.types import State
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CompiledGraph:
|
|
10
|
+
"""编译后的可执行图"""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
nodes: dict[str, StateNode],
|
|
15
|
+
edges: dict[str, str],
|
|
16
|
+
conditional_edges: dict[str, tuple[StateCondition, dict[str, str]]],
|
|
17
|
+
entry_point: str,
|
|
18
|
+
max_steps: int = 100,
|
|
19
|
+
):
|
|
20
|
+
self._nodes = nodes
|
|
21
|
+
self._edges = edges
|
|
22
|
+
self._conditional_edges = conditional_edges
|
|
23
|
+
self._entry_point = entry_point
|
|
24
|
+
self._max_steps = max_steps
|
|
25
|
+
|
|
26
|
+
def _resolve_next(self, current: str, state: State) -> str | None:
|
|
27
|
+
if current in self._conditional_edges:
|
|
28
|
+
condition, mapping = self._conditional_edges[current]
|
|
29
|
+
key = condition(state)
|
|
30
|
+
if key not in mapping:
|
|
31
|
+
raise GraphError(
|
|
32
|
+
f"Condition on '{current}' returned unmapped key '{key}', "
|
|
33
|
+
f"expected one of {list(mapping)}"
|
|
34
|
+
)
|
|
35
|
+
return mapping[key]
|
|
36
|
+
return self._edges.get(current)
|
|
37
|
+
|
|
38
|
+
async def invoke(self, state: State) -> State:
|
|
39
|
+
current: str | None = self._entry_point
|
|
40
|
+
|
|
41
|
+
for _ in range(self._max_steps):
|
|
42
|
+
if current is None or current == END:
|
|
43
|
+
break
|
|
44
|
+
if current not in self._nodes:
|
|
45
|
+
raise GraphError(f"Unknown node '{current}'")
|
|
46
|
+
state = await self._nodes[current](state)
|
|
47
|
+
current = self._resolve_next(current, state)
|
|
48
|
+
else:
|
|
49
|
+
raise GraphError(
|
|
50
|
+
f"Graph exceeded max steps ({self._max_steps}), possible infinite loop"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return state
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from neobot_chat.schema.exceptions import GraphError
|
|
4
|
+
from neobot_chat.graph.constants import END
|
|
5
|
+
from neobot_chat.graph.executor import CompiledGraph
|
|
6
|
+
from neobot_chat.graph.types import StateCondition, StateNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StateGraph:
|
|
10
|
+
"""状态图构建器"""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._nodes: dict[str, StateNode] = {}
|
|
14
|
+
self._edges: dict[str, str] = {}
|
|
15
|
+
self._conditional_edges: dict[str, tuple[StateCondition, dict[str, str]]] = {}
|
|
16
|
+
self._entry_point: str | None = None
|
|
17
|
+
|
|
18
|
+
def _assert_no_outgoing(self, node: str) -> None:
|
|
19
|
+
if node in self._edges or node in self._conditional_edges:
|
|
20
|
+
raise GraphError(f"Node '{node}' already has outgoing edges")
|
|
21
|
+
|
|
22
|
+
def add_node(self, name: str, func: StateNode) -> None:
|
|
23
|
+
if name in self._nodes:
|
|
24
|
+
raise GraphError(f"Node '{name}' already exists")
|
|
25
|
+
self._nodes[name] = func
|
|
26
|
+
|
|
27
|
+
def add_edge(self, from_node: str, to_node: str) -> None:
|
|
28
|
+
self._assert_no_outgoing(from_node)
|
|
29
|
+
self._edges[from_node] = to_node
|
|
30
|
+
|
|
31
|
+
def add_conditional_edges(
|
|
32
|
+
self, from_node: str, condition: StateCondition, mapping: dict[str, str]
|
|
33
|
+
) -> None:
|
|
34
|
+
self._assert_no_outgoing(from_node)
|
|
35
|
+
self._conditional_edges[from_node] = (condition, mapping)
|
|
36
|
+
|
|
37
|
+
def set_entry_point(self, node: str) -> None:
|
|
38
|
+
self._entry_point = node
|
|
39
|
+
|
|
40
|
+
def compile(self) -> CompiledGraph:
|
|
41
|
+
if self._entry_point is None:
|
|
42
|
+
raise GraphError("Entry point not set")
|
|
43
|
+
if self._entry_point not in self._nodes:
|
|
44
|
+
raise GraphError(f"Entry point '{self._entry_point}' not found in nodes")
|
|
45
|
+
valid = set(self._nodes) | {END}
|
|
46
|
+
for src, dst in self._edges.items():
|
|
47
|
+
if dst not in valid:
|
|
48
|
+
raise GraphError(f"Edge '{src}' -> '{dst}': target node not found")
|
|
49
|
+
for src, (_, mapping) in self._conditional_edges.items():
|
|
50
|
+
for dst in mapping.values():
|
|
51
|
+
if dst not in valid:
|
|
52
|
+
raise GraphError(
|
|
53
|
+
f"Conditional edge '{src}' -> '{dst}': target node not found"
|
|
54
|
+
)
|
|
55
|
+
return CompiledGraph(
|
|
56
|
+
nodes=self._nodes,
|
|
57
|
+
edges=self._edges,
|
|
58
|
+
conditional_edges=self._conditional_edges,
|
|
59
|
+
entry_point=self._entry_point,
|
|
60
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from neobot_chat.graph.types import StateNode
|
|
4
|
+
from neobot_chat.skills.inject import inject_skills
|
|
5
|
+
from neobot_chat.skills.registry import SkillRegistry
|
|
6
|
+
from neobot_chat.schema.types import State
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def skill_node(skills: SkillRegistry) -> StateNode:
|
|
10
|
+
"""创建一个 Graph 内置节点,自动匹配 skills 并注入 system prompt
|
|
11
|
+
|
|
12
|
+
用法::
|
|
13
|
+
|
|
14
|
+
graph.add_node("skills", skill_node(registry))
|
|
15
|
+
graph.add_edge("skills", "agent")
|
|
16
|
+
graph.set_entry_point("skills")
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
async def _node(state: State) -> State:
|
|
20
|
+
return inject_skills(skills, state)
|
|
21
|
+
|
|
22
|
+
return _node
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from neobot_chat.providers import (
|
|
7
|
+
AnthropicProvider,
|
|
8
|
+
DeepSeekOfficialProvider,
|
|
9
|
+
OpenAIProvider,
|
|
10
|
+
Provider,
|
|
11
|
+
)
|
|
12
|
+
from neobot_chat.schema.exceptions import ValidationError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class ModelPricing:
|
|
17
|
+
"""模型价格信息。"""
|
|
18
|
+
|
|
19
|
+
input_price_per_mtokens: float = 0.0
|
|
20
|
+
output_price_per_mtokens: float = 0.0
|
|
21
|
+
cache_hit_price_per_mtokens: float = 0.0
|
|
22
|
+
billing_metric: str = ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ModelSettings:
|
|
27
|
+
"""模型运行设置。"""
|
|
28
|
+
|
|
29
|
+
temperature: float | None = None
|
|
30
|
+
max_output_tokens: int | None = None
|
|
31
|
+
timeout_seconds: float = 120.0
|
|
32
|
+
top_p: float | None = None
|
|
33
|
+
frequency_penalty: float | None = None
|
|
34
|
+
presence_penalty: float | None = None
|
|
35
|
+
extra_body: dict[str, Any] = field(default_factory=dict)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _normalize_provider_kind(provider_name: str) -> str:
|
|
39
|
+
normalized = provider_name.strip().casefold().replace("-", "_")
|
|
40
|
+
if normalized in {"anthropic"}:
|
|
41
|
+
return "anthropic"
|
|
42
|
+
if normalized in {"deepseek", "deepseek_offical", "deepseek_official"}:
|
|
43
|
+
return "deepseek"
|
|
44
|
+
if normalized in {"openai"}:
|
|
45
|
+
return "openai"
|
|
46
|
+
# 默认按 OpenAI 兼容接口处理,便于接入自定义平台代理。
|
|
47
|
+
return "openai"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class RegisteredModel:
|
|
52
|
+
"""已注册模型。"""
|
|
53
|
+
|
|
54
|
+
name: str
|
|
55
|
+
description: str
|
|
56
|
+
provider_name: str
|
|
57
|
+
model_name: str
|
|
58
|
+
base_url: str
|
|
59
|
+
api_key: str
|
|
60
|
+
pricing: ModelPricing = field(default_factory=ModelPricing)
|
|
61
|
+
settings: ModelSettings = field(default_factory=ModelSettings)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def provider_kind(self) -> str:
|
|
65
|
+
return _normalize_provider_kind(self.provider_name)
|
|
66
|
+
|
|
67
|
+
def create_provider(self) -> Provider:
|
|
68
|
+
"""根据注册信息创建 Provider 实例。"""
|
|
69
|
+
if not self.base_url:
|
|
70
|
+
raise ValidationError(f"Model '{self.name}' is missing base_url")
|
|
71
|
+
if not self.api_key:
|
|
72
|
+
raise ValidationError(f"Model '{self.name}' is missing api_key")
|
|
73
|
+
|
|
74
|
+
if self.provider_kind == "anthropic":
|
|
75
|
+
return AnthropicProvider(
|
|
76
|
+
api_key=self.api_key,
|
|
77
|
+
model=self.model_name,
|
|
78
|
+
base_url=self.base_url,
|
|
79
|
+
max_tokens=self.settings.max_output_tokens or 4096,
|
|
80
|
+
timeout=self.settings.timeout_seconds,
|
|
81
|
+
temperature=self.settings.temperature,
|
|
82
|
+
top_p=self.settings.top_p,
|
|
83
|
+
extra_body=self.settings.extra_body,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if self.provider_kind == "deepseek":
|
|
87
|
+
return DeepSeekOfficialProvider(
|
|
88
|
+
api_key=self.api_key,
|
|
89
|
+
model=self.model_name,
|
|
90
|
+
base_url=self.base_url,
|
|
91
|
+
timeout=self.settings.timeout_seconds,
|
|
92
|
+
temperature=self.settings.temperature,
|
|
93
|
+
max_tokens=self.settings.max_output_tokens,
|
|
94
|
+
top_p=self.settings.top_p,
|
|
95
|
+
frequency_penalty=self.settings.frequency_penalty,
|
|
96
|
+
presence_penalty=self.settings.presence_penalty,
|
|
97
|
+
extra_body=self.settings.extra_body,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return OpenAIProvider(
|
|
101
|
+
api_key=self.api_key,
|
|
102
|
+
model=self.model_name,
|
|
103
|
+
base_url=self.base_url,
|
|
104
|
+
timeout=self.settings.timeout_seconds,
|
|
105
|
+
temperature=self.settings.temperature,
|
|
106
|
+
max_tokens=self.settings.max_output_tokens,
|
|
107
|
+
top_p=self.settings.top_p,
|
|
108
|
+
frequency_penalty=self.settings.frequency_penalty,
|
|
109
|
+
presence_penalty=self.settings.presence_penalty,
|
|
110
|
+
extra_body=self.settings.extra_body,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class ModelRegistry:
|
|
115
|
+
"""模型注册中心。"""
|
|
116
|
+
|
|
117
|
+
def __init__(self) -> None:
|
|
118
|
+
self._models: dict[str, RegisteredModel] = {}
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def names(self) -> tuple[str, ...]:
|
|
122
|
+
return tuple(self._models.keys())
|
|
123
|
+
|
|
124
|
+
def clear(self) -> None:
|
|
125
|
+
self._models.clear()
|
|
126
|
+
|
|
127
|
+
def register(self, model: RegisteredModel, *, replace: bool = True) -> None:
|
|
128
|
+
if not replace and model.name in self._models:
|
|
129
|
+
raise ValidationError(f"Model '{model.name}' is already registered")
|
|
130
|
+
self._models[model.name] = model
|
|
131
|
+
|
|
132
|
+
def get(self, name: str) -> RegisteredModel:
|
|
133
|
+
try:
|
|
134
|
+
return self._models[name]
|
|
135
|
+
except KeyError as exc:
|
|
136
|
+
raise ValidationError(f"Model '{name}' is not registered") from exc
|
|
137
|
+
|
|
138
|
+
def create_provider(self, name: str) -> Provider:
|
|
139
|
+
return self.get(name).create_provider()
|
|
140
|
+
|
|
141
|
+
def items(self) -> tuple[tuple[str, RegisteredModel], ...]:
|
|
142
|
+
return tuple(self._models.items())
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
model_registry = ModelRegistry()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_model_registry() -> ModelRegistry:
|
|
149
|
+
return model_registry
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def register_model(model: RegisteredModel, *, replace: bool = True) -> None:
|
|
153
|
+
model_registry.register(model, replace=replace)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_registered_model(name: str) -> RegisteredModel:
|
|
157
|
+
return model_registry.get(name)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def create_provider(name: str) -> Provider:
|
|
161
|
+
return model_registry.create_provider(name)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from neobot_chat.providers.anthropic import AnthropicProvider
|
|
2
|
+
from neobot_chat.providers.base import Provider
|
|
3
|
+
from neobot_chat.providers.deepseek_offical import DeepSeekOfficalProvider, DeepSeekOfficialProvider
|
|
4
|
+
from neobot_chat.providers.openai import OpenAIProvider
|
|
5
|
+
|
|
6
|
+
__all__ = ["AnthropicProvider", "DeepSeekOfficalProvider", "DeepSeekOfficialProvider", "OpenAIProvider", "Provider"]
|