python-codex 0.2.7__py3-none-any.whl → 0.3.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.
- pycodex/__init__.py +14 -14
- pycodex/agent.py +465 -499
- pycodex/bootstrap.py +417 -0
- pycodex/cli.py +236 -510
- pycodex/compat.py +19 -5
- pycodex/context.py +222 -212
- pycodex/doctor.py +52 -48
- pycodex/events.py +857 -0
- pycodex/feishu_card.py +217 -163
- pycodex/feishu_link.py +43 -83
- pycodex/model.py +324 -253
- pycodex/model_metadata.py +19 -7
- pycodex/portable.py +76 -45
- pycodex/portable_server.py +32 -24
- pycodex/prompts/models.json +245 -983
- pycodex/protocol.py +177 -137
- pycodex/runtime.py +579 -176
- pycodex/runtime_services.py +204 -157
- pycodex/tools/__init__.py +1 -1
- pycodex/tools/apply_patch_tool.py +69 -48
- pycodex/tools/base_tool.py +89 -42
- pycodex/tools/clock_tool.py +58 -25
- pycodex/tools/close_agent_tool.py +2 -2
- pycodex/tools/code_mode_manager.py +77 -64
- pycodex/tools/exec_command_tool.py +26 -11
- pycodex/tools/exec_tool.py +4 -4
- pycodex/tools/grep_files_tool.py +12 -10
- pycodex/tools/ipython_tool.py +10 -13
- pycodex/tools/list_dir_tool.py +13 -9
- pycodex/tools/read_file_tool.py +29 -17
- pycodex/tools/request_permissions_tool.py +15 -5
- pycodex/tools/request_user_input_tool.py +13 -104
- pycodex/tools/resume_agent_tool.py +2 -2
- pycodex/tools/send_input_tool.py +11 -8
- pycodex/tools/shell_command_tool.py +7 -5
- pycodex/tools/shell_tool.py +7 -5
- pycodex/tools/spawn_agent_tool.py +7 -4
- pycodex/tools/unified_exec_manager.py +102 -69
- pycodex/tools/update_plan_tool.py +8 -5
- pycodex/tools/view_image_tool.py +7 -5
- pycodex/tools/wait_agent_tool.py +27 -4
- pycodex/tools/wait_tool.py +5 -4
- pycodex/tools/web_search_tool.py +4 -2
- pycodex/tools/write_stdin_tool.py +12 -11
- pycodex/utils/__init__.py +2 -17
- pycodex/utils/compactor.py +41 -72
- pycodex/utils/debug.py +2 -2
- pycodex/utils/dotenv.py +6 -7
- pycodex/utils/event_helpers.py +190 -0
- pycodex/utils/get_env.py +27 -70
- pycodex/{image_utils.py → utils/image_utils.py} +8 -11
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/session_persist.py +217 -163
- pycodex/utils/truncation.py +21 -45
- python_codex-0.3.0.dist-info/METADATA +704 -0
- python_codex-0.3.0.dist-info/RECORD +90 -0
- responses_server/__init__.py +1 -5
- responses_server/__main__.py +0 -1
- responses_server/app.py +36 -31
- responses_server/config.py +23 -23
- responses_server/messages_api.py +51 -53
- responses_server/payload_processors.py +25 -20
- responses_server/server.py +11 -11
- responses_server/session_store.py +14 -11
- responses_server/stream_router.py +101 -98
- responses_server/tools/custom_adapter.py +17 -16
- responses_server/tools/web_search.py +39 -36
- responses_server/trajectory_dump.py +36 -14
- workspace_server/__main__.py +0 -1
- workspace_server/app.py +461 -375
- workspace_server/workspace.html +852 -228
- workspace_server/workspaces.html +94 -95
- workspace_server/workspaces.py +137 -79
- pycodex/collaboration.py +0 -20
- pycodex/interactive_session.py +0 -415
- pycodex/prompts/collaboration_default.md +0 -11
- pycodex/prompts/collaboration_plan.md +0 -128
- pycodex/utils/toolcall_visualize.py +0 -713
- pycodex/utils/visualize.py +0 -560
- python_codex-0.2.7.dist-info/METADATA +0 -455
- python_codex-0.2.7.dist-info/RECORD +0 -93
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/WHEEL +0 -0
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/entry_points.txt +0 -0
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
pycodex/__init__.py,sha256=4BBGUXImsNBdSQjp2KtshkjQK9BMz5gK93DzpWokkWY,3235
|
|
2
|
+
pycodex/agent.py,sha256=zCIG2DV0oQntLXHFj8FVRkgBp5TCC0YJ7sz0lni9J1s,21177
|
|
3
|
+
pycodex/bootstrap.py,sha256=DSVx8u94tHRYbUa_e9zdKPqozOv_CrfctbTKZ4JG064,13765
|
|
4
|
+
pycodex/cli.py,sha256=m5IJhnodNqsfMTIzdwbuaHBl2r-N8x1MxYSpe_hlHiM,12997
|
|
5
|
+
pycodex/compat.py,sha256=yoQQWMiAQ6ytZv3DAMIbp66ImA0kyY4G5eKHrtwmj6c,3490
|
|
6
|
+
pycodex/context.py,sha256=nkq2Y0coZNpWs5IJj7uVVT65fDqh58Z9c07y_Wy0f4Q,26604
|
|
7
|
+
pycodex/doctor.py,sha256=9p624b1zloXbhyMRi1-Of1YtnYeoF4zHxfGFhfujDS4,10620
|
|
8
|
+
pycodex/events.py,sha256=MZljtqra6-Hnt9Emjbn_wgp5cGaODx1w3CbmIKd5htI,29692
|
|
9
|
+
pycodex/feishu_card.py,sha256=Gclo4TI3KTfdT5NDLiv58CRg6hH3sUCRXuSrHgXgYjI,27767
|
|
10
|
+
pycodex/feishu_link.py,sha256=yUbEvO-8g-Bxo8_cF9s6vLZguz3NA6q3qt2A9Q09fsE,14523
|
|
11
|
+
pycodex/model.py,sha256=cKG2STZ_Kc3ThelY6KTTRkBTCrIwcmF74Rhfhj71N08,44175
|
|
12
|
+
pycodex/model_metadata.py,sha256=RMYhvgkm2v1nhCslSiDXPAwrRAx8FPz9QJqv8QTbWlc,1332
|
|
13
|
+
pycodex/portable.py,sha256=rfOpVnBZ01RS0Kyjo_w2FNKHY2t-lUcHVtWhPsbp1-k,16158
|
|
14
|
+
pycodex/portable_server.py,sha256=A4IY3xHtBQLRxdHQ4O5zdaVGjqENaLvypsSkLGZvCZ0,7498
|
|
15
|
+
pycodex/protocol.py,sha256=dRcTVKTaE7Eaq_jiSYdCzBMebIo3gnL7wB1x-NJqY5E,12027
|
|
16
|
+
pycodex/runtime.py,sha256=bRlqrmXqK6kPndAG96QoGALKicbWmfuXgo2jDvbQzdE,23806
|
|
17
|
+
pycodex/runtime_services.py,sha256=4jDCMlXHwgbJZPJkWzVEPCRHPxrD0mpCBbDInVToGG8,15033
|
|
18
|
+
pycodex/prompts/default_base_instructions.md,sha256=D65mcj6bo4CDvVom-D9cbJRJVNquo0NghKt164_fRsg,20923
|
|
19
|
+
pycodex/prompts/models.json,sha256=9UUBlKwpQSrt53WS-D--I3pQVBhRUe9XhDpEHUS6be8,330811
|
|
20
|
+
pycodex/prompts/permissions/approval_policy/never.md,sha256=QceTG6wjkaJARjYr0HYV1aPnPcpGcrkRUW-smWRr6MQ,120
|
|
21
|
+
pycodex/prompts/permissions/approval_policy/on_failure.md,sha256=dfJjpXkpO6_ANdCKxbVJ8o4vyLxevrJWfKsGHTqtbkc,289
|
|
22
|
+
pycodex/prompts/permissions/approval_policy/on_request.md,sha256=hVQalzh0FAdkKzw5u-N4H7-LtC9ijVDlYsh3OKsZKzo,3661
|
|
23
|
+
pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md,sha256=mOinishp1k-wlPsaEuIOMn5GoVm_dAIsWIuEMmv2r7o,1725
|
|
24
|
+
pycodex/prompts/permissions/approval_policy/unless_trusted.md,sha256=XHpi1Lfx1iIXFbbQ_ho_kGstA3JN-RLho291HM30UNw,247
|
|
25
|
+
pycodex/prompts/permissions/sandbox_mode/danger_full_access.md,sha256=nZ7YHacBd3cAHKRZc9XClOOOnXJPXPh0WFBueh5C2D0,197
|
|
26
|
+
pycodex/prompts/permissions/sandbox_mode/read_only.md,sha256=2rAPEXsBYCcuttI5j3euS-3uv_v97catIsnhxlSQSIM,173
|
|
27
|
+
pycodex/prompts/permissions/sandbox_mode/workspace_write.md,sha256=lVN-LwrBbHqlv5yVjcd_mU8tzZW8jfKpTatJKIZu9HI,277
|
|
28
|
+
pycodex/tools/__init__.py,sha256=UYwmsX_Ckw_YaYQ6TYhUU6w5pxi2qzZoTrq6BAgNErM,1857
|
|
29
|
+
pycodex/tools/agent_tool_schemas.py,sha256=8cL9ml4So0H6IIeK0ZzjAo9bk3VeprDOSYPpIamOqEU,2048
|
|
30
|
+
pycodex/tools/apply_patch_tool.py,sha256=t8XFCGZ7q-s9C1Ty6qkReBRk-QD3ONvcKXLAz9djKRY,13956
|
|
31
|
+
pycodex/tools/base_tool.py,sha256=kNNzPWijtdl4C_GunyR5G1qvsr7KQtnFJ2RiRHHnAYo,6478
|
|
32
|
+
pycodex/tools/clock_tool.py,sha256=2_RYbBEw4V9_f1L-qZl3hhJ6XNPM_gBDmOlCh1cCAjo,6575
|
|
33
|
+
pycodex/tools/close_agent_tool.py,sha256=zQtvd5iGW6YCPFiec8zc2OdGtgNIxoA4p2ZTuCoJOaI,2031
|
|
34
|
+
pycodex/tools/code_mode_manager.py,sha256=VFVn7S_qUHCOHW952CMeGxTIE-7CPOx2uuXJZgBml8o,19160
|
|
35
|
+
pycodex/tools/exec_command_tool.py,sha256=iqRiKaaX-AjG6LjATAMOakHIP0gpCWCj2o8af_8eAiQ,4464
|
|
36
|
+
pycodex/tools/exec_runtime.js,sha256=DR1uocKailTqNWAcJNFJuQgFFMSUzTpT_uQsRaneg2s,3643
|
|
37
|
+
pycodex/tools/exec_tool.py,sha256=tuC21rlhP8CPSYml4XnSDSRV0UPGkQ6aHj0DLIoOOvM,2274
|
|
38
|
+
pycodex/tools/grep_files_tool.py,sha256=Ysw68VKI9OEvUH5haYx8FWuthkHykc1wskbuzjj8gTk,5293
|
|
39
|
+
pycodex/tools/ipython_tool.py,sha256=SKVYpJEwlYjOQZQWbpqvrA-3K9aJTlIG9KM50p5JYUM,4496
|
|
40
|
+
pycodex/tools/list_dir_tool.py,sha256=_U6ouRcVbGHT5mChVyUsiMgDGrFK7EFMFaspPLAHtH8,5337
|
|
41
|
+
pycodex/tools/read_file_tool.py,sha256=YK__uhIVldBjAQega5S_ZuZU5jgP_kLOwr-YhjAmRo4,9735
|
|
42
|
+
pycodex/tools/request_permissions_tool.py,sha256=n8eg29SgRbx1DUxxLbAztnc5i-0j2N2WnDupspjwWbE,3979
|
|
43
|
+
pycodex/tools/request_user_input_tool.py,sha256=_7siHF2p3KiNDFwsTqKKIUw2kPCXMzLaR12PkZ1wdIQ,4050
|
|
44
|
+
pycodex/tools/resume_agent_tool.py,sha256=D_OcWQbIbsWf-2NrRgDab4hB6BUbay0XidfPInhg88Y,1614
|
|
45
|
+
pycodex/tools/send_input_tool.py,sha256=J91LLz4RpvHmBWti0XZgI9xWipWwD8-1hQIdC8-Jd5E,3762
|
|
46
|
+
pycodex/tools/shell_command_tool.py,sha256=US63I5xiV5zfO1Oox2yHUg7F6XoKzmyxtohfaQIsmrw,4216
|
|
47
|
+
pycodex/tools/shell_tool.py,sha256=lqFH2XosfBg7yQKhpB7i4vLGz9sNQtEZihD0rjsmBCQ,4068
|
|
48
|
+
pycodex/tools/spawn_agent_tool.py,sha256=n_WZi1IX4Waj-GQ-hR1BVlk3zzw_ssvem7zcj3tYI38,6209
|
|
49
|
+
pycodex/tools/unified_exec_manager.py,sha256=sO1H3uoXyZ6DiyF98-eidQ77mS-P5gSWyPqM3L50yTQ,14538
|
|
50
|
+
pycodex/tools/update_plan_tool.py,sha256=8zyANrQgj8u2LBJ2yz_0Cg5jP3S6ibp6eASxUupX9C8,3199
|
|
51
|
+
pycodex/tools/view_image_tool.py,sha256=UZRkBEwuCkqxOw7XBwacdgPZAtbnD6uSHTdm-6amUo4,3902
|
|
52
|
+
pycodex/tools/wait_agent_tool.py,sha256=2OWfb9FCGu5SjbRp33dVEJWG3apMtQgczvQR1V_rm6I,3829
|
|
53
|
+
pycodex/tools/wait_tool.py,sha256=0_OO_DvMsBhG3oDKZePTOjZTBq1lmWntXAzqIqvebdA,3206
|
|
54
|
+
pycodex/tools/web_search_tool.py,sha256=3ao1IvJw7MwcP35UKLbqrM-nlm5Z-UUFVdEzkJYOV1o,988
|
|
55
|
+
pycodex/tools/write_stdin_tool.py,sha256=jvBDchLLmoDITd7g-ZVeEVXHRhWKkmnjM5laL-53FOM,3609
|
|
56
|
+
pycodex/utils/__init__.py,sha256=lbf-jbo6JS7t1wfFkQqLHlG4zZVqXYGkOIj2cvtrjx8,591
|
|
57
|
+
pycodex/utils/async_bridge.py,sha256=d21Pjim-nsQbSG5pJddd0WaQ03CzA3w3TINWDmmjWbg,1815
|
|
58
|
+
pycodex/utils/compactor.py,sha256=0tlszw1n54aMEGH6sL1U1dEgFSU5ioYL2NI5uSvHSiM,6119
|
|
59
|
+
pycodex/utils/debug.py,sha256=BdXmuAQVWVB1BONKRDlkMpgXpkt4M1jhvEAq5n2AumI,296
|
|
60
|
+
pycodex/utils/dotenv.py,sha256=h7ZgvqpEswetqjhtwfiaHyokAHwNKPakizTAHPHz5Ds,1865
|
|
61
|
+
pycodex/utils/event_helpers.py,sha256=pnxL0-nBC7cnOheHPCIfkHqJjMbkJg3C348_EzAVhFA,6342
|
|
62
|
+
pycodex/utils/get_env.py,sha256=L4S8h84tkgxZKvrxY7ezTgePbXepG-yIigUTKeY6uuE,6094
|
|
63
|
+
pycodex/utils/image_utils.py,sha256=1kXhtTqL4xtazpEABHhtvJBUJlceo8hSzg6ukmf_4FA,2407
|
|
64
|
+
pycodex/utils/random_ids.py,sha256=jLTxZZ0MBbRdQOFDFWGVRjWzIo6FKSQ5ADXbQSLPp1s,379
|
|
65
|
+
pycodex/utils/session_persist.py,sha256=v9KhmjUB2yCg55c85i8ZU1BWfc-WEMDPmpT0Rv4K8a8,22489
|
|
66
|
+
pycodex/utils/truncation.py,sha256=5D6vAorT8EgXgjd7ASwtraAyIZdlCR-w5jKZqJxDzho,5446
|
|
67
|
+
responses_server/__init__.py,sha256=sx9C88YWqmehASZy24FUMqT14A36doM_rj0MIZtQ9iQ,377
|
|
68
|
+
responses_server/__main__.py,sha256=876xZTqhPeDBD2GKKr8pKX33Af8PhFTosAI8bYQsFgQ,61
|
|
69
|
+
responses_server/app.py,sha256=DtA1Sck-6FDyCww3MLkLIMs-fDacY1d-lqZLWoUn-KM,7874
|
|
70
|
+
responses_server/config.py,sha256=sCJYGisPHW9Uup26-R9Zv0o9W_kRVt-ETc2MFCVjG90,2795
|
|
71
|
+
responses_server/messages_api.py,sha256=ialAYTMmN0y-uxQB3Y51ukq-kvkSVO84WVLXaqLsKw0,18775
|
|
72
|
+
responses_server/payload_processors.py,sha256=dqPwqHD-FhyiIwjzFEMf2wCtQDxkRyerZzbdqnH1aDE,3561
|
|
73
|
+
responses_server/server.py,sha256=k4DbsOYkaHHcy38uF9u_QXahJC1cplKsTu-MdC3DCIs,2286
|
|
74
|
+
responses_server/session_store.py,sha256=nwGxQL6nUVKnDVeDHz8IzqNQ7dWk8Ybf0URekOHdxTo,1133
|
|
75
|
+
responses_server/stream_router.py,sha256=xoHwegjziLOgrzvkdAtJbKGoSBn6sBF-unUSo6QxStc,41402
|
|
76
|
+
responses_server/trajectory_dump.py,sha256=5_Wy6tslszrqx3j1JeP_boyTbzIicKy8No84PzMnmHk,5111
|
|
77
|
+
responses_server/tools/__init__.py,sha256=ivsBSEy0SBUhY-Uea5v1XMLXShkwHdCVl0id-1FwdZg,150
|
|
78
|
+
responses_server/tools/custom_adapter.py,sha256=SnIiNUdomff3APtTriURAC9YCeEdhCDjzGMEYusfUdk,8340
|
|
79
|
+
responses_server/tools/web_search.py,sha256=pnKH9JyTScRehWpZS3HpXRvjWsQvC0QZqxjEOCT4ndg,8936
|
|
80
|
+
workspace_server/__init__.py,sha256=PRM4ONODb6hxjBUHkBD4TprE_pkTCIlT9sfftK5ic6M,866
|
|
81
|
+
workspace_server/__main__.py,sha256=876xZTqhPeDBD2GKKr8pKX33Af8PhFTosAI8bYQsFgQ,61
|
|
82
|
+
workspace_server/app.py,sha256=iYUMziweCnsLX0EyT3jnswyVM4x2FhacrlinhBhymHo,59130
|
|
83
|
+
workspace_server/workspace.html,sha256=6UmGemYZfxFFX1qZNhGsVmD_BDWu6qhoPy3zyfd5_po,71483
|
|
84
|
+
workspace_server/workspaces.html,sha256=TpmfzpuCIXTVdFQzQrojxSCtBTHMSESn1zNGOmNz_W8,15787
|
|
85
|
+
workspace_server/workspaces.py,sha256=0oOk1xj7FxoJkLe2OyFDYVwWV4HHyNAwVaF-3Ln3NiY,22660
|
|
86
|
+
python_codex-0.3.0.dist-info/METADATA,sha256=Crsk3zCk6n-qeqnK7DoyH0GkSP4uUdf2f3TR5rTtUBs,35836
|
|
87
|
+
python_codex-0.3.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
88
|
+
python_codex-0.3.0.dist-info/entry_points.txt,sha256=vkV2UWCtEKvQNMJuPNjt8HyBKiwp83JyqBatrBNGDp8,80
|
|
89
|
+
python_codex-0.3.0.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
|
|
90
|
+
python_codex-0.3.0.dist-info/RECORD,,
|
responses_server/__init__.py
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
from .app import
|
|
2
|
-
ManagedResponseServer,
|
|
3
|
-
launch_chat_completion_compat_server,
|
|
4
|
-
run_server,
|
|
5
|
-
)
|
|
1
|
+
from .app import ManagedResponseServer, launch_chat_completion_compat_server, run_server
|
|
6
2
|
from .config import CompatServerConfig
|
|
7
3
|
from .server import ResponseServer
|
|
8
4
|
from .stream_router import StreamRouter
|
responses_server/__main__.py
CHANGED
responses_server/app.py
CHANGED
|
@@ -1,44 +1,52 @@
|
|
|
1
|
-
|
|
2
1
|
import argparse
|
|
3
2
|
import asyncio
|
|
4
|
-
from dataclasses import replace
|
|
5
3
|
import json
|
|
6
4
|
import socket
|
|
7
5
|
import threading
|
|
8
6
|
import time
|
|
7
|
+
import typing
|
|
8
|
+
from dataclasses import replace
|
|
9
9
|
from typing import Iterator
|
|
10
10
|
|
|
11
|
+
import uvicorn
|
|
11
12
|
from fastapi import FastAPI, Request
|
|
12
13
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
13
|
-
import uvicorn
|
|
14
14
|
|
|
15
15
|
from .config import CompatServerConfig
|
|
16
16
|
from .server import ResponseServer
|
|
17
17
|
from .stream_router import OutcommingChatError, UnsupportedIncommingFeature
|
|
18
|
-
import typing
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
def _run_uvicorn_server(server) ->
|
|
20
|
+
def _run_uvicorn_server(server) -> "None":
|
|
22
21
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
|
23
22
|
server.run()
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
def _format_sse_event(
|
|
25
|
+
def _format_sse_event(
|
|
26
|
+
event_name: "str", payload: "typing.Dict[str, object]"
|
|
27
|
+
) -> "bytes":
|
|
27
28
|
data = json.dumps(payload, ensure_ascii=False)
|
|
28
29
|
return f"event: {event_name}\ndata: {data}\n\n".encode("utf-8")
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def _stream_events(
|
|
32
|
+
def _stream_events(
|
|
33
|
+
response_server: "ResponseServer",
|
|
34
|
+
request_body: "typing.Dict[str, object]",
|
|
35
|
+
request_headers: "typing.Dict[str, str]",
|
|
36
|
+
) -> "Iterator[bytes]":
|
|
32
37
|
try:
|
|
33
|
-
event_iter = response_server.start_response_stream(
|
|
38
|
+
event_iter = response_server.start_response_stream(
|
|
39
|
+
request_body, request_headers
|
|
40
|
+
)
|
|
34
41
|
for event_name, payload in event_iter:
|
|
35
42
|
yield _format_sse_event(event_name, payload)
|
|
36
43
|
except OutcommingChatError as exc:
|
|
37
|
-
|
|
44
|
+
|
|
38
45
|
import traceback
|
|
46
|
+
|
|
39
47
|
exc_info = traceback.format_exception(type(exc), exc, exc.__traceback__)
|
|
40
48
|
error = {
|
|
41
|
-
"message":
|
|
49
|
+
"message": "\n".join(exc_info),
|
|
42
50
|
}
|
|
43
51
|
error_type = getattr(exc, "error_type", None)
|
|
44
52
|
if error_type:
|
|
@@ -54,7 +62,7 @@ def _stream_events(response_server: 'ResponseServer', request_body: 'typing.Dict
|
|
|
54
62
|
)
|
|
55
63
|
|
|
56
64
|
|
|
57
|
-
def build_parser() ->
|
|
65
|
+
def build_parser() -> "argparse.ArgumentParser":
|
|
58
66
|
parser = argparse.ArgumentParser(
|
|
59
67
|
prog="python -m responses_server",
|
|
60
68
|
description=(
|
|
@@ -77,7 +85,7 @@ def build_parser() -> 'argparse.ArgumentParser':
|
|
|
77
85
|
return parser
|
|
78
86
|
|
|
79
87
|
|
|
80
|
-
def run_server(config:
|
|
88
|
+
def run_server(config: "CompatServerConfig") -> "None":
|
|
81
89
|
uvicorn.run(
|
|
82
90
|
ManagedResponseServer.build_app(config),
|
|
83
91
|
host=config.host,
|
|
@@ -87,10 +95,10 @@ def run_server(config: 'CompatServerConfig') -> 'None':
|
|
|
87
95
|
|
|
88
96
|
|
|
89
97
|
def launch_chat_completion_compat_server(
|
|
90
|
-
base_url:
|
|
91
|
-
api_key_env:
|
|
92
|
-
model_provider:
|
|
93
|
-
outcomming_api:
|
|
98
|
+
base_url: "str",
|
|
99
|
+
api_key_env: "typing.Union[str, None]" = None,
|
|
100
|
+
model_provider: "typing.Union[str, None]" = None,
|
|
101
|
+
outcomming_api: "str" = "chat_completions",
|
|
94
102
|
):
|
|
95
103
|
config = CompatServerConfig.from_base_url(
|
|
96
104
|
base_url,
|
|
@@ -106,10 +114,10 @@ def launch_chat_completion_compat_server(
|
|
|
106
114
|
class ManagedResponseServer:
|
|
107
115
|
@staticmethod
|
|
108
116
|
def build_app(
|
|
109
|
-
config:
|
|
117
|
+
config: "CompatServerConfig",
|
|
110
118
|
session_store=None,
|
|
111
119
|
stream_router=None,
|
|
112
|
-
) ->
|
|
120
|
+
) -> "FastAPI":
|
|
113
121
|
response_server = ResponseServer(
|
|
114
122
|
config,
|
|
115
123
|
session_store=session_store,
|
|
@@ -120,7 +128,7 @@ class ManagedResponseServer:
|
|
|
120
128
|
|
|
121
129
|
@app.get("/health")
|
|
122
130
|
@app.get("/healthz")
|
|
123
|
-
async def health() ->
|
|
131
|
+
async def health() -> "typing.Dict[str, bool]":
|
|
124
132
|
return {"ok": True}
|
|
125
133
|
|
|
126
134
|
@app.get("/models")
|
|
@@ -136,7 +144,7 @@ class ManagedResponseServer:
|
|
|
136
144
|
|
|
137
145
|
@app.post("/responses")
|
|
138
146
|
@app.post("/v1/responses")
|
|
139
|
-
async def responses(request:
|
|
147
|
+
async def responses(request: "Request"):
|
|
140
148
|
try:
|
|
141
149
|
request_body = await request.json()
|
|
142
150
|
except Exception as exc:
|
|
@@ -151,8 +159,7 @@ class ManagedResponseServer:
|
|
|
151
159
|
)
|
|
152
160
|
|
|
153
161
|
request_headers = {
|
|
154
|
-
str(key).lower(): str(value)
|
|
155
|
-
for key, value in request.headers.items()
|
|
162
|
+
str(key).lower(): str(value) for key, value in request.headers.items()
|
|
156
163
|
}
|
|
157
164
|
try:
|
|
158
165
|
response_server.stream_router.validate_incomming_request(request_body)
|
|
@@ -173,7 +180,7 @@ class ManagedResponseServer:
|
|
|
173
180
|
|
|
174
181
|
return app
|
|
175
182
|
|
|
176
|
-
def __init__(self, config:
|
|
183
|
+
def __init__(self, config: "CompatServerConfig") -> "None":
|
|
177
184
|
port = config.port or _reserve_free_port()
|
|
178
185
|
self._config = replace(config, port=port)
|
|
179
186
|
self._app = self.build_app(self._config)
|
|
@@ -193,10 +200,10 @@ class ManagedResponseServer:
|
|
|
193
200
|
)
|
|
194
201
|
|
|
195
202
|
@property
|
|
196
|
-
def base_url(self) ->
|
|
203
|
+
def base_url(self) -> "str":
|
|
197
204
|
return f"http://{self._config.host}:{self._config.port}/v1"
|
|
198
205
|
|
|
199
|
-
def start(self, timeout_seconds:
|
|
206
|
+
def start(self, timeout_seconds: "float" = 10.0) -> "None":
|
|
200
207
|
self._thread.start()
|
|
201
208
|
deadline = time.time() + timeout_seconds
|
|
202
209
|
while not self._server.started:
|
|
@@ -206,16 +213,14 @@ class ManagedResponseServer:
|
|
|
206
213
|
)
|
|
207
214
|
time.sleep(0.01)
|
|
208
215
|
|
|
209
|
-
def stop(self, timeout_seconds:
|
|
216
|
+
def stop(self, timeout_seconds: "float" = 5.0) -> "None":
|
|
210
217
|
self._server.should_exit = True
|
|
211
218
|
self._thread.join(timeout=timeout_seconds)
|
|
212
219
|
if self._thread.is_alive():
|
|
213
|
-
raise RuntimeError(
|
|
214
|
-
"timed out waiting for managed responses server to stop"
|
|
215
|
-
)
|
|
220
|
+
raise RuntimeError("timed out waiting for managed responses server to stop")
|
|
216
221
|
|
|
217
222
|
|
|
218
|
-
def main() ->
|
|
223
|
+
def main() -> "None":
|
|
219
224
|
args = build_parser().parse_args()
|
|
220
225
|
run_server(
|
|
221
226
|
CompatServerConfig(
|
|
@@ -234,7 +239,7 @@ if __name__ == "__main__":
|
|
|
234
239
|
main()
|
|
235
240
|
|
|
236
241
|
|
|
237
|
-
def _reserve_free_port() ->
|
|
242
|
+
def _reserve_free_port() -> "int":
|
|
238
243
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
239
244
|
try:
|
|
240
245
|
sock.bind(("127.0.0.1", 0))
|
responses_server/config.py
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
|
|
2
1
|
import os
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
import urllib.parse
|
|
5
2
|
import typing
|
|
6
|
-
|
|
3
|
+
import urllib.parse
|
|
4
|
+
from dataclasses import dataclass
|
|
7
5
|
|
|
8
6
|
DEFAULT_OUTCOMMING_TIMEOUT_SECONDS = 300.0
|
|
9
7
|
|
|
10
8
|
|
|
11
|
-
@dataclass(
|
|
9
|
+
@dataclass(
|
|
10
|
+
frozen=True,
|
|
11
|
+
)
|
|
12
12
|
class CompatServerConfig:
|
|
13
|
-
host:
|
|
14
|
-
port:
|
|
15
|
-
outcomming_base_url:
|
|
16
|
-
outcomming_api:
|
|
17
|
-
outcomming_api_key_env:
|
|
18
|
-
model_provider:
|
|
19
|
-
timeout_seconds:
|
|
13
|
+
host: "str" = "127.0.0.1"
|
|
14
|
+
port: "int" = 0
|
|
15
|
+
outcomming_base_url: "str" = "http://127.0.0.1:8000/v1"
|
|
16
|
+
outcomming_api: "str" = "chat_completions"
|
|
17
|
+
outcomming_api_key_env: "typing.Union[str, None]" = None
|
|
18
|
+
model_provider: "typing.Union[str, None]" = None
|
|
19
|
+
timeout_seconds: "float" = DEFAULT_OUTCOMMING_TIMEOUT_SECONDS
|
|
20
20
|
|
|
21
|
-
def outcomming_api_key(self) ->
|
|
21
|
+
def outcomming_api_key(self) -> "typing.Union[str, None]":
|
|
22
22
|
if self.outcomming_api_key_env is None:
|
|
23
23
|
return None
|
|
24
24
|
value = os.environ.get(self.outcomming_api_key_env, "").strip()
|
|
25
25
|
return value or None
|
|
26
26
|
|
|
27
|
-
def outcomming_chat_completions_url(self) ->
|
|
27
|
+
def outcomming_chat_completions_url(self) -> "str":
|
|
28
28
|
base = self.outcomming_base_url.rstrip("/")
|
|
29
29
|
return f"{base}/chat/completions"
|
|
30
30
|
|
|
31
|
-
def outcomming_messages_url(self) ->
|
|
31
|
+
def outcomming_messages_url(self) -> "str":
|
|
32
32
|
base = self.outcomming_base_url.rstrip("/")
|
|
33
33
|
return f"{base}/messages"
|
|
34
34
|
|
|
35
|
-
def outcomming_models_url(self) ->
|
|
35
|
+
def outcomming_models_url(self) -> "str":
|
|
36
36
|
base = self.outcomming_base_url.rstrip("/")
|
|
37
37
|
return f"{base}/models"
|
|
38
38
|
|
|
39
|
-
def normalized_outcomming_api(self) ->
|
|
39
|
+
def normalized_outcomming_api(self) -> "str":
|
|
40
40
|
value = str(self.outcomming_api or "").strip().lower()
|
|
41
41
|
return value or "chat_completions"
|
|
42
42
|
|
|
43
|
-
def with_ephemeral_port(self) ->
|
|
43
|
+
def with_ephemeral_port(self) -> "CompatServerConfig":
|
|
44
44
|
return CompatServerConfig(
|
|
45
45
|
host=self.host,
|
|
46
46
|
port=0,
|
|
@@ -54,11 +54,11 @@ class CompatServerConfig:
|
|
|
54
54
|
@classmethod
|
|
55
55
|
def from_base_url(
|
|
56
56
|
cls,
|
|
57
|
-
outcomming_base_url:
|
|
58
|
-
api_key_env:
|
|
59
|
-
model_provider:
|
|
60
|
-
outcomming_api:
|
|
61
|
-
) ->
|
|
57
|
+
outcomming_base_url: "str",
|
|
58
|
+
api_key_env: "typing.Union[str, None]" = None,
|
|
59
|
+
model_provider: "typing.Union[str, None]" = None,
|
|
60
|
+
outcomming_api: "str" = "chat_completions",
|
|
61
|
+
) -> "CompatServerConfig":
|
|
62
62
|
parsed = urllib.parse.urlparse(outcomming_base_url)
|
|
63
63
|
if not parsed.scheme or not parsed.netloc:
|
|
64
64
|
raise ValueError(f"invalid outcomming base url: {outcomming_base_url}")
|
responses_server/messages_api.py
CHANGED
|
@@ -9,8 +9,8 @@ class MessagesAPIAdapterError(ValueError):
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def build_messages_request(
|
|
12
|
-
outcomming_request:
|
|
13
|
-
) ->
|
|
12
|
+
outcomming_request: "typing.Dict[str, object]",
|
|
13
|
+
) -> "typing.Dict[str, object]":
|
|
14
14
|
model = str(outcomming_request.get("model", "")).strip()
|
|
15
15
|
if not model:
|
|
16
16
|
raise MessagesAPIAdapterError("outcomming request is missing `model`")
|
|
@@ -19,13 +19,11 @@ def build_messages_request(
|
|
|
19
19
|
if not isinstance(raw_messages, list):
|
|
20
20
|
raise MessagesAPIAdapterError("outcomming request `messages` must be a list")
|
|
21
21
|
|
|
22
|
-
system_blocks:
|
|
23
|
-
messages:
|
|
22
|
+
system_blocks: "typing.List[typing.Dict[str, object]]" = []
|
|
23
|
+
messages: "typing.List[typing.Dict[str, object]]" = []
|
|
24
24
|
for raw_message in raw_messages:
|
|
25
25
|
if not isinstance(raw_message, dict):
|
|
26
|
-
raise MessagesAPIAdapterError(
|
|
27
|
-
"outcomming request messages must be objects"
|
|
28
|
-
)
|
|
26
|
+
raise MessagesAPIAdapterError("outcomming request messages must be objects")
|
|
29
27
|
role = str(raw_message.get("role", "")).strip()
|
|
30
28
|
if role in {"developer", "system"}:
|
|
31
29
|
text = str(raw_message.get("content", "") or "")
|
|
@@ -60,7 +58,7 @@ def build_messages_request(
|
|
|
60
58
|
f"unsupported outcomming message role for messages API: {role!r}"
|
|
61
59
|
)
|
|
62
60
|
|
|
63
|
-
payload:
|
|
61
|
+
payload: "typing.Dict[str, object]" = {
|
|
64
62
|
"model": model,
|
|
65
63
|
"messages": messages,
|
|
66
64
|
"max_tokens": _resolve_max_tokens(outcomming_request),
|
|
@@ -84,12 +82,12 @@ def build_messages_request(
|
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
def iter_chat_chunks(
|
|
87
|
-
event_name:
|
|
88
|
-
payload:
|
|
89
|
-
state:
|
|
90
|
-
) ->
|
|
85
|
+
event_name: "typing.Union[str, None]",
|
|
86
|
+
payload: "typing.Dict[str, object]",
|
|
87
|
+
state: "typing.Dict[str, object]",
|
|
88
|
+
) -> "typing.List[typing.Dict[str, object]]":
|
|
91
89
|
event_type = str(payload.get("type") or event_name or "").strip()
|
|
92
|
-
chunks:
|
|
90
|
+
chunks: "typing.List[typing.Dict[str, object]]" = []
|
|
93
91
|
|
|
94
92
|
if event_type == "message_start":
|
|
95
93
|
message = payload.get("message") or {}
|
|
@@ -188,13 +186,15 @@ def iter_chat_chunks(
|
|
|
188
186
|
return chunks
|
|
189
187
|
|
|
190
188
|
|
|
191
|
-
def saw_message_stop(state:
|
|
189
|
+
def saw_message_stop(state: "typing.Dict[str, object]") -> "bool":
|
|
192
190
|
return bool(state.get("saw_message_stop"))
|
|
193
191
|
|
|
194
192
|
|
|
195
|
-
def _build_text_blocks(
|
|
193
|
+
def _build_text_blocks(
|
|
194
|
+
raw_content: "object",
|
|
195
|
+
) -> "typing.List[typing.Dict[str, object]]":
|
|
196
196
|
if isinstance(raw_content, list):
|
|
197
|
-
blocks:
|
|
197
|
+
blocks: "typing.List[typing.Dict[str, object]]" = []
|
|
198
198
|
for raw_part in raw_content:
|
|
199
199
|
if not isinstance(raw_part, dict):
|
|
200
200
|
raise MessagesAPIAdapterError("message content parts must be objects")
|
|
@@ -219,8 +219,8 @@ def _build_text_blocks(raw_content: 'object') -> 'typing.List[typing.Dict[str, o
|
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
def _build_image_block(
|
|
222
|
-
raw_part:
|
|
223
|
-
) ->
|
|
222
|
+
raw_part: "typing.Dict[str, object]",
|
|
223
|
+
) -> "typing.Dict[str, object]":
|
|
224
224
|
image_url = raw_part.get("image_url") or {}
|
|
225
225
|
if not isinstance(image_url, dict):
|
|
226
226
|
raise MessagesAPIAdapterError("`image_url` content parts must be objects")
|
|
@@ -233,7 +233,7 @@ def _build_image_block(
|
|
|
233
233
|
return {"type": "image", "source": {"type": "url", "url": url}}
|
|
234
234
|
|
|
235
235
|
header, _, data = url.partition(",")
|
|
236
|
-
media_type = header[len("data:"):].split(";")[0].strip()
|
|
236
|
+
media_type = header[len("data:") :].split(";")[0].strip()
|
|
237
237
|
if not media_type or not header.endswith(";base64"):
|
|
238
238
|
raise MessagesAPIAdapterError(
|
|
239
239
|
"`image_url` data URLs must be base64 encoded with a media type"
|
|
@@ -249,9 +249,9 @@ def _build_image_block(
|
|
|
249
249
|
|
|
250
250
|
|
|
251
251
|
def _build_assistant_blocks(
|
|
252
|
-
raw_message:
|
|
253
|
-
) ->
|
|
254
|
-
blocks:
|
|
252
|
+
raw_message: "typing.Dict[str, object]",
|
|
253
|
+
) -> "typing.List[typing.Dict[str, object]]":
|
|
254
|
+
blocks: "typing.List[typing.Dict[str, object]]" = []
|
|
255
255
|
reasoning = str(raw_message.get("reasoning", "") or "")
|
|
256
256
|
if reasoning:
|
|
257
257
|
blocks.append({"type": "thinking", "thinking": reasoning})
|
|
@@ -284,8 +284,8 @@ def _build_assistant_blocks(
|
|
|
284
284
|
|
|
285
285
|
|
|
286
286
|
def _build_tool_result_block(
|
|
287
|
-
raw_message:
|
|
288
|
-
) ->
|
|
287
|
+
raw_message: "typing.Dict[str, object]",
|
|
288
|
+
) -> "typing.Dict[str, object]":
|
|
289
289
|
return {
|
|
290
290
|
"type": "tool_result",
|
|
291
291
|
"tool_use_id": str(raw_message.get("tool_call_id", "")).strip(),
|
|
@@ -294,9 +294,9 @@ def _build_tool_result_block(
|
|
|
294
294
|
|
|
295
295
|
|
|
296
296
|
def _translate_tools(
|
|
297
|
-
raw_tools:
|
|
298
|
-
) ->
|
|
299
|
-
translated:
|
|
297
|
+
raw_tools: "object",
|
|
298
|
+
) -> "typing.List[typing.Dict[str, object]]":
|
|
299
|
+
translated: "typing.List[typing.Dict[str, object]]" = []
|
|
300
300
|
if not isinstance(raw_tools, list):
|
|
301
301
|
return translated
|
|
302
302
|
for raw_tool in raw_tools:
|
|
@@ -321,9 +321,9 @@ def _translate_tools(
|
|
|
321
321
|
|
|
322
322
|
|
|
323
323
|
def _translate_tool_choice(
|
|
324
|
-
raw_tool_choice:
|
|
325
|
-
parallel_tool_calls:
|
|
326
|
-
) ->
|
|
324
|
+
raw_tool_choice: "object",
|
|
325
|
+
parallel_tool_calls: "object",
|
|
326
|
+
) -> "typing.Union[typing.Dict[str, object], None]":
|
|
327
327
|
if raw_tool_choice is None:
|
|
328
328
|
if parallel_tool_calls is False:
|
|
329
329
|
return {
|
|
@@ -332,7 +332,7 @@ def _translate_tool_choice(
|
|
|
332
332
|
}
|
|
333
333
|
return None
|
|
334
334
|
|
|
335
|
-
translated:
|
|
335
|
+
translated: "typing.Dict[str, object]"
|
|
336
336
|
if isinstance(raw_tool_choice, str):
|
|
337
337
|
choice = raw_tool_choice.strip()
|
|
338
338
|
if choice == "auto":
|
|
@@ -355,9 +355,7 @@ def _translate_tool_choice(
|
|
|
355
355
|
if not name:
|
|
356
356
|
name = str(raw_tool_choice.get("name", "")).strip()
|
|
357
357
|
if not name:
|
|
358
|
-
raise MessagesAPIAdapterError(
|
|
359
|
-
"function tool_choice is missing `name`"
|
|
360
|
-
)
|
|
358
|
+
raise MessagesAPIAdapterError("function tool_choice is missing `name`")
|
|
361
359
|
translated = {
|
|
362
360
|
"type": "tool",
|
|
363
361
|
"name": name,
|
|
@@ -376,7 +374,7 @@ def _translate_tool_choice(
|
|
|
376
374
|
return translated
|
|
377
375
|
|
|
378
376
|
|
|
379
|
-
def _parse_json_object(raw_value:
|
|
377
|
+
def _parse_json_object(raw_value: "object") -> "typing.Dict[str, object]":
|
|
380
378
|
if isinstance(raw_value, dict):
|
|
381
379
|
return dict(raw_value)
|
|
382
380
|
if isinstance(raw_value, str):
|
|
@@ -399,7 +397,7 @@ def _parse_json_object(raw_value: 'object') -> 'typing.Dict[str, object]':
|
|
|
399
397
|
)
|
|
400
398
|
|
|
401
399
|
|
|
402
|
-
def _resolve_max_tokens(outcomming_request:
|
|
400
|
+
def _resolve_max_tokens(outcomming_request: "typing.Dict[str, object]") -> "int":
|
|
403
401
|
raw_value = outcomming_request.get("max_tokens")
|
|
404
402
|
if isinstance(raw_value, bool):
|
|
405
403
|
return DEFAULT_MESSAGES_MAX_TOKENS
|
|
@@ -408,7 +406,7 @@ def _resolve_max_tokens(outcomming_request: 'typing.Dict[str, object]') -> 'int'
|
|
|
408
406
|
return DEFAULT_MESSAGES_MAX_TOKENS
|
|
409
407
|
|
|
410
408
|
|
|
411
|
-
def _usage_chunk(raw_usage:
|
|
409
|
+
def _usage_chunk(raw_usage: "object") -> "typing.Union[typing.Dict[str, object], None]":
|
|
412
410
|
usage = _translate_usage(raw_usage)
|
|
413
411
|
if not usage:
|
|
414
412
|
return None
|
|
@@ -418,10 +416,10 @@ def _usage_chunk(raw_usage: 'object') -> 'typing.Union[typing.Dict[str, object],
|
|
|
418
416
|
}
|
|
419
417
|
|
|
420
418
|
|
|
421
|
-
def _translate_usage(raw_usage:
|
|
419
|
+
def _translate_usage(raw_usage: "object") -> "typing.Dict[str, object]":
|
|
422
420
|
if not isinstance(raw_usage, dict):
|
|
423
421
|
return {}
|
|
424
|
-
usage:
|
|
422
|
+
usage: "typing.Dict[str, object]" = {}
|
|
425
423
|
input_tokens = raw_usage.get("input_tokens")
|
|
426
424
|
output_tokens = raw_usage.get("output_tokens")
|
|
427
425
|
if isinstance(input_tokens, int):
|
|
@@ -434,7 +432,7 @@ def _translate_usage(raw_usage: 'object') -> 'typing.Dict[str, object]':
|
|
|
434
432
|
elif isinstance(input_tokens, int) and isinstance(output_tokens, int):
|
|
435
433
|
usage["total_tokens"] = input_tokens + output_tokens
|
|
436
434
|
|
|
437
|
-
input_details:
|
|
435
|
+
input_details: "typing.Dict[str, int]" = {}
|
|
438
436
|
cache_creation = raw_usage.get("cache_creation_input_tokens")
|
|
439
437
|
if isinstance(cache_creation, int):
|
|
440
438
|
input_details["cache_creation_input_tokens"] = cache_creation
|
|
@@ -446,7 +444,7 @@ def _translate_usage(raw_usage: 'object') -> 'typing.Dict[str, object]':
|
|
|
446
444
|
return usage
|
|
447
445
|
|
|
448
446
|
|
|
449
|
-
def _normalize_index(raw_index:
|
|
447
|
+
def _normalize_index(raw_index: "object") -> "int":
|
|
450
448
|
if isinstance(raw_index, int):
|
|
451
449
|
return raw_index
|
|
452
450
|
try:
|
|
@@ -455,7 +453,7 @@ def _normalize_index(raw_index: 'object') -> 'int':
|
|
|
455
453
|
return 0
|
|
456
454
|
|
|
457
455
|
|
|
458
|
-
def _translate_stop_reason(raw_stop_reason:
|
|
456
|
+
def _translate_stop_reason(raw_stop_reason: "object") -> "typing.Union[str, None]":
|
|
459
457
|
if not isinstance(raw_stop_reason, str):
|
|
460
458
|
return None
|
|
461
459
|
stop_reason = raw_stop_reason.strip()
|
|
@@ -470,21 +468,21 @@ def _translate_stop_reason(raw_stop_reason: 'object') -> 'typing.Union[str, None
|
|
|
470
468
|
return stop_reason
|
|
471
469
|
|
|
472
470
|
|
|
473
|
-
def _chat_text_chunk(text:
|
|
471
|
+
def _chat_text_chunk(text: "str") -> "typing.Dict[str, object]":
|
|
474
472
|
return _chat_delta_chunk({"content": text})
|
|
475
473
|
|
|
476
474
|
|
|
477
|
-
def _chat_reasoning_chunk(reasoning:
|
|
475
|
+
def _chat_reasoning_chunk(reasoning: "str") -> "typing.Dict[str, object]":
|
|
478
476
|
return _chat_delta_chunk({"reasoning_content": reasoning})
|
|
479
477
|
|
|
480
478
|
|
|
481
479
|
def _chat_tool_chunk(
|
|
482
|
-
index:
|
|
483
|
-
call_id:
|
|
484
|
-
name:
|
|
485
|
-
arguments:
|
|
486
|
-
) ->
|
|
487
|
-
tool_call:
|
|
480
|
+
index: "int",
|
|
481
|
+
call_id: "str" = "",
|
|
482
|
+
name: "str" = "",
|
|
483
|
+
arguments: "str" = "",
|
|
484
|
+
) -> "typing.Dict[str, object]":
|
|
485
|
+
tool_call: "typing.Dict[str, object]" = {
|
|
488
486
|
"index": index,
|
|
489
487
|
"function": {},
|
|
490
488
|
}
|
|
@@ -502,7 +500,7 @@ def _chat_tool_chunk(
|
|
|
502
500
|
return _chat_delta_chunk({"tool_calls": [tool_call]})
|
|
503
501
|
|
|
504
502
|
|
|
505
|
-
def _chat_delta_chunk(delta:
|
|
503
|
+
def _chat_delta_chunk(delta: "typing.Dict[str, object]") -> "typing.Dict[str, object]":
|
|
506
504
|
return {
|
|
507
505
|
"choices": [
|
|
508
506
|
{
|
|
@@ -514,7 +512,7 @@ def _chat_delta_chunk(delta: 'typing.Dict[str, object]') -> 'typing.Dict[str, ob
|
|
|
514
512
|
}
|
|
515
513
|
|
|
516
514
|
|
|
517
|
-
def _chat_finish_chunk(finish_reason:
|
|
515
|
+
def _chat_finish_chunk(finish_reason: "str") -> "typing.Dict[str, object]":
|
|
518
516
|
return {
|
|
519
517
|
"choices": [
|
|
520
518
|
{
|
|
@@ -526,5 +524,5 @@ def _chat_finish_chunk(finish_reason: 'str') -> 'typing.Dict[str, object]':
|
|
|
526
524
|
}
|
|
527
525
|
|
|
528
526
|
|
|
529
|
-
def _dump_json(raw_value:
|
|
527
|
+
def _dump_json(raw_value: "object") -> "str":
|
|
530
528
|
return json.dumps(raw_value, ensure_ascii=False, separators=(",", ":"))
|