agstack 1.0.8__tar.gz → 1.2.0__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.
- {agstack-1.0.8 → agstack-1.2.0}/PKG-INFO +5 -5
- {agstack-1.0.8 → agstack-1.2.0}/agstack/config/logger.py +8 -4
- agstack-1.2.0/agstack/contexts.py +21 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/decorators.py +9 -4
- {agstack-1.0.8 → agstack-1.2.0}/agstack/events.py +4 -4
- {agstack-1.0.8 → agstack-1.2.0}/agstack/fastapi/middleware.py +8 -2
- {agstack-1.0.8 → agstack-1.2.0}/agstack/infra/db/__init__.py +2 -2
- {agstack-1.0.8 → agstack-1.2.0}/agstack/infra/es/__init__.py +2 -2
- {agstack-1.0.8 → agstack-1.2.0}/agstack/infra/kg/__init__.py +4 -3
- {agstack-1.0.8 → agstack-1.2.0}/agstack/infra/mq/__init__.py +1 -1
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/client.py +17 -5
- {agstack-1.0.8 → agstack-1.2.0}/agstack.egg-info/PKG-INFO +5 -5
- {agstack-1.0.8 → agstack-1.2.0}/agstack.egg-info/SOURCES.txt +1 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack.egg-info/requires.txt +4 -4
- {agstack-1.0.8 → agstack-1.2.0}/pyproject.toml +7 -6
- {agstack-1.0.8 → agstack-1.2.0}/LICENSE +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/README.md +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/config/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/config/manager.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/config/types.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/exceptions.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/fastapi/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/fastapi/exception.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/fastapi/offline.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/fastapi/sse.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/agent.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/context.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/events.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/exceptions.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/factory.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/flow.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/loader.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/records.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/registry.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/state.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/flow/tool.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/prompts.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/llm/token.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/registry.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/schema.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/security/__init__.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/security/casbin.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/security/crypt.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack/status.py +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack.egg-info/dependency_links.txt +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/agstack.egg-info/top_level.txt +0 -0
- {agstack-1.0.8 → agstack-1.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agstack
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Production-ready toolkit for building FastAPI and LLM applications
|
|
5
5
|
Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
6
6
|
Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
@@ -24,18 +24,18 @@ Requires-Dist: ag-ui-protocol>=0.1.13
|
|
|
24
24
|
Requires-Dist: aio-pika>=9.6.1
|
|
25
25
|
Requires-Dist: asyncpg>=0.30.0
|
|
26
26
|
Requires-Dist: elasticsearch[async]>=9.3.0
|
|
27
|
-
Requires-Dist: fastapi>=0.133.
|
|
27
|
+
Requires-Dist: fastapi>=0.133.1
|
|
28
28
|
Requires-Dist: jwcrypto>=1.5.6
|
|
29
29
|
Requires-Dist: loguru>=0.7.3
|
|
30
30
|
Requires-Dist: nebula3-python>=3.8.3
|
|
31
|
-
Requires-Dist: openai>=2.
|
|
31
|
+
Requires-Dist: openai>=2.26.0
|
|
32
32
|
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
33
33
|
Requires-Dist: pycasbin>=2.8.0
|
|
34
34
|
Requires-Dist: pydantic>=2.12.4
|
|
35
35
|
Requires-Dist: python-multipart>=0.0.20
|
|
36
36
|
Requires-Dist: requests>=2.32.5
|
|
37
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.0.
|
|
38
|
-
Requires-Dist: sqlobjects>=1.2.
|
|
37
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.48
|
|
38
|
+
Requires-Dist: sqlobjects>=1.2.5
|
|
39
39
|
Requires-Dist: tiktoken>=0.12.0
|
|
40
40
|
Requires-Dist: uvicorn>=0.41.0
|
|
41
41
|
Dynamic: license-file
|
|
@@ -26,16 +26,20 @@ class InterceptHandler(logging.Handler):
|
|
|
26
26
|
level = record.levelno
|
|
27
27
|
|
|
28
28
|
# 直接使用 logging.LogRecord 的信息,避免栈帧查找
|
|
29
|
-
logger.patch(
|
|
29
|
+
patched = logger.patch(
|
|
30
30
|
lambda r: r.update(
|
|
31
31
|
name=record.name,
|
|
32
|
-
file={"name": record.pathname, "path": record.pathname}, # type: ignore
|
|
32
|
+
file={"name": record.pathname, "path": record.pathname}, # type: ignore
|
|
33
33
|
line=record.lineno,
|
|
34
34
|
function=record.funcName,
|
|
35
35
|
)
|
|
36
|
-
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if record.exc_info:
|
|
39
|
+
patched.opt(exception=record.exc_info).log(level, record.getMessage())
|
|
40
|
+
else:
|
|
41
|
+
patched.log(level, record.getMessage())
|
|
37
42
|
|
|
38
|
-
# CRITICAL/FATAL 级别自动退出
|
|
39
43
|
if record.levelno >= logging.CRITICAL:
|
|
40
44
|
sys.exit(1)
|
|
41
45
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright (c) 2020-2025 XtraVisions, All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""运行时上下文"""
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from contextvars import ContextVar, Token
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_request_id: ContextVar[str | None] = ContextVar("request_id", default=None)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_request_id() -> str:
|
|
13
|
+
return _request_id.get() or str(uuid.uuid4())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def set_request_id(value: str) -> Token:
|
|
17
|
+
return _request_id.set(value)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def reset_request_id(token: Token) -> None:
|
|
21
|
+
_request_id.reset(token)
|
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import time
|
|
5
5
|
from functools import wraps
|
|
6
6
|
from logging import Logger
|
|
7
|
+
from typing import Awaitable, Callable, ParamSpec, TypeVar
|
|
7
8
|
|
|
8
9
|
from sqlobjects.session import ctx_session, has_session
|
|
9
10
|
|
|
@@ -49,17 +50,21 @@ def autoretry(
|
|
|
49
50
|
return decorator
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
P = ParamSpec("P")
|
|
54
|
+
R = TypeVar("R")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def with_session(func: Callable[P, Awaitable[R]], dbname: str | None = None) -> Callable[P, Awaitable[R]]:
|
|
53
58
|
"""装饰器:确保方法在 session 上下文中执行,支持嵌套调用"""
|
|
54
59
|
|
|
55
60
|
@wraps(func)
|
|
56
|
-
async def wrapper(
|
|
61
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
57
62
|
# 如果已经在 session 上下文中,直接执行
|
|
58
63
|
if has_session(dbname):
|
|
59
|
-
return await func(
|
|
64
|
+
return await func(*args, **kwargs)
|
|
60
65
|
|
|
61
66
|
# 否则创建新的 session 上下文
|
|
62
67
|
async with ctx_session(dbname):
|
|
63
|
-
return await func(
|
|
68
|
+
return await func(*args, **kwargs)
|
|
64
69
|
|
|
65
70
|
return wrapper
|
|
@@ -13,10 +13,10 @@ class EventType(str, Enum):
|
|
|
13
13
|
"""事件类型定义"""
|
|
14
14
|
|
|
15
15
|
# Infra 生命周期
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
DB_INITED = "db.inited"
|
|
17
|
+
ES_INITED = "es.inited"
|
|
18
|
+
MQ_INITED = "mq.inited"
|
|
19
|
+
KG_INITED = "kg.inited"
|
|
20
20
|
|
|
21
21
|
# 组件生命周期
|
|
22
22
|
COMPONENT_REGISTERED = "component.registered"
|
|
@@ -8,6 +8,8 @@ from typing import Callable
|
|
|
8
8
|
from fastapi import Request, Response
|
|
9
9
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
10
10
|
|
|
11
|
+
from ..contexts import reset_request_id, set_request_id
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
class RequestIDMiddleware(BaseHTTPMiddleware):
|
|
13
15
|
"""请求 ID 中间件
|
|
@@ -22,8 +24,12 @@ class RequestIDMiddleware(BaseHTTPMiddleware):
|
|
|
22
24
|
# 将 request_id 存储到 request.state(供后续使用)
|
|
23
25
|
request.state.request_id = request_id
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
token = set_request_id(request_id)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
response = await call_next(request)
|
|
31
|
+
finally:
|
|
32
|
+
reset_request_id(token)
|
|
27
33
|
|
|
28
34
|
# 在响应头中返回 request_id
|
|
29
35
|
response.headers["X-Request-ID"] = request_id
|
|
@@ -27,7 +27,7 @@ async def setup_db(
|
|
|
27
27
|
**engine_kwargs: Any,
|
|
28
28
|
):
|
|
29
29
|
url = f"postgresql+asyncpg://{username}:{password}@{host}/{database}"
|
|
30
|
-
await init_db(
|
|
30
|
+
db = await init_db(
|
|
31
31
|
url,
|
|
32
32
|
echo=echo,
|
|
33
33
|
pool_size=pool_size,
|
|
@@ -36,7 +36,7 @@ async def setup_db(
|
|
|
36
36
|
pool_recycle=pool_recycle,
|
|
37
37
|
**engine_kwargs,
|
|
38
38
|
)
|
|
39
|
-
await event_bus.publish(EventType.
|
|
39
|
+
await event_bus.publish(EventType.DB_INITED, {"db": db})
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
async def shutdown_db():
|
|
@@ -14,7 +14,7 @@ async def setup_es(
|
|
|
14
14
|
sniff_timeout: float = 3,
|
|
15
15
|
):
|
|
16
16
|
# 为 elasticsearch-dsl 创建连接
|
|
17
|
-
async_connections.connections.create_connection(
|
|
17
|
+
es = async_connections.connections.create_connection(
|
|
18
18
|
alias="default",
|
|
19
19
|
hosts=hosts,
|
|
20
20
|
verify_certs=False,
|
|
@@ -24,7 +24,7 @@ async def setup_es(
|
|
|
24
24
|
sniff_on_node_failure=True,
|
|
25
25
|
http_auth=(username, password),
|
|
26
26
|
)
|
|
27
|
-
await event_bus.publish(EventType.
|
|
27
|
+
await event_bus.publish(EventType.ES_INITED, {"es": es})
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
async def shutdown_es():
|
|
@@ -42,17 +42,18 @@ async def setup_kg(
|
|
|
42
42
|
if not pool.init(hosts, config):
|
|
43
43
|
raise RuntimeError("Failed to initialize NebulaGraph connection pool")
|
|
44
44
|
|
|
45
|
-
#
|
|
45
|
+
# 验证连接,space 不存在时自动创建
|
|
46
46
|
session = pool.get_session(username, password)
|
|
47
47
|
try:
|
|
48
|
-
|
|
48
|
+
session.execute(f"CREATE SPACE IF NOT EXISTS `{space}` (vid_type=FIXED_STRING(64))")
|
|
49
|
+
result = session.execute(f"USE `{space}`")
|
|
49
50
|
if not result.is_succeeded():
|
|
50
51
|
raise RuntimeError(f"Failed to use space '{space}': {result.error_msg()}")
|
|
51
52
|
finally:
|
|
52
53
|
session.release()
|
|
53
54
|
|
|
54
55
|
_context = _KGContext(pool=pool, space=space, username=username, password=password)
|
|
55
|
-
await event_bus.publish(EventType.
|
|
56
|
+
await event_bus.publish(EventType.KG_INITED, {"pool": pool})
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
async def shutdown_kg():
|
|
@@ -20,7 +20,7 @@ async def setup_mq(host: str, port: int, username: str, password: str):
|
|
|
20
20
|
connection_url = f"amqp://{username}:{password}@{host}:{port}/"
|
|
21
21
|
_connection = await aio_pika.connect_robust(connection_url, loop=get_event_loop())
|
|
22
22
|
|
|
23
|
-
await event_bus.publish(EventType.
|
|
23
|
+
await event_bus.publish(EventType.MQ_INITED, {"connection": _connection})
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
async def shutdown_mq():
|
|
@@ -5,9 +5,12 @@ import time
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any, AsyncIterator, Literal, overload
|
|
6
6
|
|
|
7
7
|
from httpx import AsyncClient
|
|
8
|
+
from httpx import Timeout as HttpxTimeout
|
|
8
9
|
from openai import APIError, APITimeoutError, AsyncOpenAI, OpenAI, RateLimitError
|
|
10
|
+
from openai import Timeout as OpenAITimeout
|
|
9
11
|
from openai.types.chat import ChatCompletionMessageParam
|
|
10
12
|
|
|
13
|
+
from ..contexts import get_request_id
|
|
11
14
|
from ..exceptions import AppException
|
|
12
15
|
|
|
13
16
|
|
|
@@ -51,16 +54,20 @@ class LLMClient:
|
|
|
51
54
|
"""
|
|
52
55
|
self._base_url = base_url
|
|
53
56
|
|
|
57
|
+
# 只限制连接超时,读取不限(推理时间不可预估)
|
|
58
|
+
_openai_timeout = OpenAITimeout(connect=10.0, read=None, write=None, pool=None)
|
|
59
|
+
_httpx_timeout = HttpxTimeout(connect=10.0, read=None, write=None, pool=None)
|
|
60
|
+
|
|
54
61
|
# 异步客户端
|
|
55
62
|
self._async_client = AsyncOpenAI(
|
|
56
63
|
base_url=base_url,
|
|
57
64
|
api_key=api_key,
|
|
58
|
-
timeout=
|
|
65
|
+
timeout=_openai_timeout,
|
|
59
66
|
max_retries=0, # 手动控制重试
|
|
60
67
|
)
|
|
61
68
|
self._async_http_client = AsyncClient(
|
|
62
69
|
headers={"Authorization": f"Bearer {api_key}"},
|
|
63
|
-
timeout=
|
|
70
|
+
timeout=_httpx_timeout,
|
|
64
71
|
)
|
|
65
72
|
|
|
66
73
|
# 同步客户端(延迟初始化)
|
|
@@ -69,7 +76,7 @@ class LLMClient:
|
|
|
69
76
|
self._sync_client_config = {
|
|
70
77
|
"base_url": base_url,
|
|
71
78
|
"api_key": api_key,
|
|
72
|
-
"timeout":
|
|
79
|
+
"timeout": _openai_timeout,
|
|
73
80
|
"max_retries": 0,
|
|
74
81
|
}
|
|
75
82
|
|
|
@@ -150,6 +157,7 @@ class LLMClient:
|
|
|
150
157
|
messages=messages,
|
|
151
158
|
temperature=temperature,
|
|
152
159
|
max_tokens=max_tokens,
|
|
160
|
+
extra_headers={"X-Request-ID": get_request_id()},
|
|
153
161
|
**kwargs,
|
|
154
162
|
)
|
|
155
163
|
|
|
@@ -255,6 +263,7 @@ class LLMClient:
|
|
|
255
263
|
max_tokens=max_tokens,
|
|
256
264
|
stream=True,
|
|
257
265
|
stream_options={"include_usage": True},
|
|
266
|
+
extra_headers={"X-Request-ID": get_request_id()},
|
|
258
267
|
**kwargs,
|
|
259
268
|
)
|
|
260
269
|
|
|
@@ -293,7 +302,9 @@ class LLMClient:
|
|
|
293
302
|
exceptions=(APITimeoutError, ConnectionError),
|
|
294
303
|
)
|
|
295
304
|
async def _call():
|
|
296
|
-
return await self._async_client.embeddings.create(
|
|
305
|
+
return await self._async_client.embeddings.create(
|
|
306
|
+
model=model, input=texts, extra_headers={"X-Request-ID": get_request_id()}
|
|
307
|
+
)
|
|
297
308
|
|
|
298
309
|
try:
|
|
299
310
|
response = await _call()
|
|
@@ -447,6 +458,7 @@ class LLMClient:
|
|
|
447
458
|
"top_n": top_n,
|
|
448
459
|
"return_documents": True,
|
|
449
460
|
},
|
|
461
|
+
headers={"X-Request-ID": get_request_id()},
|
|
450
462
|
)
|
|
451
463
|
|
|
452
464
|
try:
|
|
@@ -495,7 +507,7 @@ class LLMClient:
|
|
|
495
507
|
"top_n": top_n,
|
|
496
508
|
"return_documents": True,
|
|
497
509
|
},
|
|
498
|
-
timeout=
|
|
510
|
+
timeout=None,
|
|
499
511
|
)
|
|
500
512
|
response.raise_for_status()
|
|
501
513
|
data = response.json()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agstack
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Production-ready toolkit for building FastAPI and LLM applications
|
|
5
5
|
Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
6
6
|
Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
@@ -24,18 +24,18 @@ Requires-Dist: ag-ui-protocol>=0.1.13
|
|
|
24
24
|
Requires-Dist: aio-pika>=9.6.1
|
|
25
25
|
Requires-Dist: asyncpg>=0.30.0
|
|
26
26
|
Requires-Dist: elasticsearch[async]>=9.3.0
|
|
27
|
-
Requires-Dist: fastapi>=0.133.
|
|
27
|
+
Requires-Dist: fastapi>=0.133.1
|
|
28
28
|
Requires-Dist: jwcrypto>=1.5.6
|
|
29
29
|
Requires-Dist: loguru>=0.7.3
|
|
30
30
|
Requires-Dist: nebula3-python>=3.8.3
|
|
31
|
-
Requires-Dist: openai>=2.
|
|
31
|
+
Requires-Dist: openai>=2.26.0
|
|
32
32
|
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
33
33
|
Requires-Dist: pycasbin>=2.8.0
|
|
34
34
|
Requires-Dist: pydantic>=2.12.4
|
|
35
35
|
Requires-Dist: python-multipart>=0.0.20
|
|
36
36
|
Requires-Dist: requests>=2.32.5
|
|
37
|
-
Requires-Dist: sqlalchemy[asyncio]>=2.0.
|
|
38
|
-
Requires-Dist: sqlobjects>=1.2.
|
|
37
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.48
|
|
38
|
+
Requires-Dist: sqlobjects>=1.2.5
|
|
39
39
|
Requires-Dist: tiktoken>=0.12.0
|
|
40
40
|
Requires-Dist: uvicorn>=0.41.0
|
|
41
41
|
Dynamic: license-file
|
|
@@ -2,17 +2,17 @@ ag-ui-protocol>=0.1.13
|
|
|
2
2
|
aio-pika>=9.6.1
|
|
3
3
|
asyncpg>=0.30.0
|
|
4
4
|
elasticsearch[async]>=9.3.0
|
|
5
|
-
fastapi>=0.133.
|
|
5
|
+
fastapi>=0.133.1
|
|
6
6
|
jwcrypto>=1.5.6
|
|
7
7
|
loguru>=0.7.3
|
|
8
8
|
nebula3-python>=3.8.3
|
|
9
|
-
openai>=2.
|
|
9
|
+
openai>=2.26.0
|
|
10
10
|
passlib[bcrypt]>=1.7.4
|
|
11
11
|
pycasbin>=2.8.0
|
|
12
12
|
pydantic>=2.12.4
|
|
13
13
|
python-multipart>=0.0.20
|
|
14
14
|
requests>=2.32.5
|
|
15
|
-
sqlalchemy[asyncio]>=2.0.
|
|
16
|
-
sqlobjects>=1.2.
|
|
15
|
+
sqlalchemy[asyncio]>=2.0.48
|
|
16
|
+
sqlobjects>=1.2.5
|
|
17
17
|
tiktoken>=0.12.0
|
|
18
18
|
uvicorn>=0.41.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agstack"
|
|
3
|
-
version = "1.0
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "Production-ready toolkit for building FastAPI and LLM applications"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -43,18 +43,18 @@ dependencies = [
|
|
|
43
43
|
"aio-pika>=9.6.1",
|
|
44
44
|
"asyncpg>=0.30.0",
|
|
45
45
|
"elasticsearch[async]>=9.3.0",
|
|
46
|
-
"fastapi>=0.133.
|
|
46
|
+
"fastapi>=0.133.1",
|
|
47
47
|
"jwcrypto>=1.5.6",
|
|
48
48
|
"loguru>=0.7.3",
|
|
49
49
|
"nebula3-python>=3.8.3",
|
|
50
|
-
"openai>=2.
|
|
50
|
+
"openai>=2.26.0",
|
|
51
51
|
"passlib[bcrypt]>=1.7.4",
|
|
52
52
|
"pycasbin>=2.8.0",
|
|
53
53
|
"pydantic>=2.12.4",
|
|
54
54
|
"python-multipart>=0.0.20",
|
|
55
55
|
"requests>=2.32.5",
|
|
56
|
-
"sqlalchemy[asyncio]>=2.0.
|
|
57
|
-
"sqlobjects>=1.2.
|
|
56
|
+
"sqlalchemy[asyncio]>=2.0.48",
|
|
57
|
+
"sqlobjects>=1.2.5",
|
|
58
58
|
"tiktoken>=0.12.0",
|
|
59
59
|
"uvicorn>=0.41.0",
|
|
60
60
|
]
|
|
@@ -62,7 +62,7 @@ dependencies = [
|
|
|
62
62
|
dev = [
|
|
63
63
|
"pre-commit>=4.4.0",
|
|
64
64
|
"pyright>=1.1.407",
|
|
65
|
-
"ruff>=0.15.
|
|
65
|
+
"ruff>=0.15.5",
|
|
66
66
|
"setuptools>=82.0.0",
|
|
67
67
|
]
|
|
68
68
|
|
|
@@ -96,6 +96,7 @@ select = [
|
|
|
96
96
|
]
|
|
97
97
|
ignore = [
|
|
98
98
|
"UP046", # disable none pep695 generic class
|
|
99
|
+
"UP047", # disable none pep695 generic function
|
|
99
100
|
"UP035", # disable using `type` instead `types.Type`
|
|
100
101
|
]
|
|
101
102
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|