python-codex 0.2.6__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.
Files changed (84) hide show
  1. pycodex/__init__.py +18 -14
  2. pycodex/agent.py +468 -462
  3. pycodex/bootstrap.py +417 -0
  4. pycodex/cli.py +236 -436
  5. pycodex/compat.py +19 -5
  6. pycodex/context.py +222 -212
  7. pycodex/doctor.py +52 -48
  8. pycodex/events.py +857 -0
  9. pycodex/feishu_card.py +217 -163
  10. pycodex/feishu_link.py +43 -83
  11. pycodex/model.py +329 -252
  12. pycodex/model_metadata.py +19 -7
  13. pycodex/portable.py +90 -52
  14. pycodex/portable_server.py +32 -24
  15. pycodex/prompts/models.json +235 -803
  16. pycodex/protocol.py +177 -137
  17. pycodex/runtime.py +579 -174
  18. pycodex/runtime_services.py +204 -157
  19. pycodex/tools/__init__.py +4 -1
  20. pycodex/tools/apply_patch_tool.py +69 -48
  21. pycodex/tools/base_tool.py +89 -42
  22. pycodex/tools/clock_tool.py +201 -0
  23. pycodex/tools/close_agent_tool.py +2 -2
  24. pycodex/tools/code_mode_manager.py +77 -64
  25. pycodex/tools/exec_command_tool.py +26 -11
  26. pycodex/tools/exec_tool.py +4 -4
  27. pycodex/tools/grep_files_tool.py +12 -10
  28. pycodex/tools/ipython_tool.py +10 -13
  29. pycodex/tools/list_dir_tool.py +13 -9
  30. pycodex/tools/read_file_tool.py +29 -17
  31. pycodex/tools/request_permissions_tool.py +15 -5
  32. pycodex/tools/request_user_input_tool.py +13 -104
  33. pycodex/tools/resume_agent_tool.py +2 -2
  34. pycodex/tools/send_input_tool.py +11 -8
  35. pycodex/tools/shell_command_tool.py +7 -5
  36. pycodex/tools/shell_tool.py +7 -5
  37. pycodex/tools/spawn_agent_tool.py +7 -4
  38. pycodex/tools/unified_exec_manager.py +102 -69
  39. pycodex/tools/update_plan_tool.py +8 -5
  40. pycodex/tools/view_image_tool.py +13 -13
  41. pycodex/tools/wait_agent_tool.py +27 -4
  42. pycodex/tools/wait_tool.py +5 -4
  43. pycodex/tools/web_search_tool.py +4 -2
  44. pycodex/tools/write_stdin_tool.py +12 -11
  45. pycodex/utils/__init__.py +2 -17
  46. pycodex/utils/compactor.py +50 -66
  47. pycodex/utils/debug.py +2 -2
  48. pycodex/utils/dotenv.py +6 -7
  49. pycodex/utils/event_helpers.py +190 -0
  50. pycodex/utils/get_env.py +27 -70
  51. pycodex/utils/image_utils.py +76 -0
  52. pycodex/utils/random_ids.py +1 -2
  53. pycodex/utils/session_persist.py +263 -161
  54. pycodex/utils/truncation.py +21 -45
  55. python_codex-0.3.0.dist-info/METADATA +704 -0
  56. python_codex-0.3.0.dist-info/RECORD +90 -0
  57. responses_server/__init__.py +1 -5
  58. responses_server/__main__.py +0 -1
  59. responses_server/app.py +36 -31
  60. responses_server/config.py +25 -22
  61. responses_server/messages_api.py +96 -49
  62. responses_server/payload_processors.py +25 -19
  63. responses_server/server.py +11 -11
  64. responses_server/session_store.py +14 -11
  65. responses_server/stream_router.py +196 -107
  66. responses_server/tools/custom_adapter.py +17 -16
  67. responses_server/tools/web_search.py +39 -36
  68. responses_server/trajectory_dump.py +51 -13
  69. workspace_server/__main__.py +0 -1
  70. workspace_server/app.py +470 -384
  71. workspace_server/workspace.html +859 -232
  72. workspace_server/workspaces.html +94 -95
  73. workspace_server/workspaces.py +168 -100
  74. pycodex/collaboration.py +0 -20
  75. pycodex/interactive_session.py +0 -415
  76. pycodex/prompts/collaboration_default.md +0 -11
  77. pycodex/prompts/collaboration_plan.md +0 -128
  78. pycodex/utils/toolcall_visualize.py +0 -713
  79. pycodex/utils/visualize.py +0 -553
  80. python_codex-0.2.6.dist-info/METADATA +0 -441
  81. python_codex-0.2.6.dist-info/RECORD +0 -91
  82. {python_codex-0.2.6.dist-info → python_codex-0.3.0.dist-info}/WHEEL +0 -0
  83. {python_codex-0.2.6.dist-info → python_codex-0.3.0.dist-info}/entry_points.txt +0 -0
  84. {python_codex-0.2.6.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,,
@@ -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
@@ -1,5 +1,4 @@
1
1
  from .app import main
2
2
 
3
-
4
3
  if __name__ == "__main__":
5
4
  main()
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) -> 'None':
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(event_name: 'str', payload: 'typing.Dict[str, object]') -> 'bytes':
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(response_server: 'ResponseServer', request_body: 'typing.Dict[str, object]', request_headers: 'typing.Dict[str, str]') -> 'Iterator[bytes]':
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(request_body, request_headers)
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": '\n'.join(exc_info),
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() -> 'argparse.ArgumentParser':
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: 'CompatServerConfig') -> 'None':
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: 'str',
91
- api_key_env: 'typing.Union[str, None]' = None,
92
- model_provider: 'typing.Union[str, None]' = None,
93
- outcomming_api: 'str' = "chat_completions",
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: 'CompatServerConfig',
117
+ config: "CompatServerConfig",
110
118
  session_store=None,
111
119
  stream_router=None,
112
- ) -> 'FastAPI':
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() -> 'typing.Dict[str, bool]':
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: '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: 'CompatServerConfig') -> 'None':
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) -> 'str':
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: 'float' = 10.0) -> 'None':
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: 'float' = 5.0) -> 'None':
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() -> 'None':
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() -> 'int':
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))
@@ -1,43 +1,46 @@
1
-
2
1
  import os
3
- from dataclasses import dataclass
4
- import urllib.parse
5
2
  import typing
3
+ import urllib.parse
4
+ from dataclasses import dataclass
5
+
6
+ DEFAULT_OUTCOMMING_TIMEOUT_SECONDS = 300.0
6
7
 
7
8
 
8
- @dataclass(frozen=True, )
9
+ @dataclass(
10
+ frozen=True,
11
+ )
9
12
  class CompatServerConfig:
10
- host: 'str' = "127.0.0.1"
11
- port: 'int' = 0
12
- outcomming_base_url: 'str' = "http://127.0.0.1:8000/v1"
13
- outcomming_api: 'str' = "chat_completions"
14
- outcomming_api_key_env: 'typing.Union[str, None]' = None
15
- model_provider: 'typing.Union[str, None]' = None
16
- timeout_seconds: 'float' = 120.0
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
17
20
 
18
- def outcomming_api_key(self) -> 'typing.Union[str, None]':
21
+ def outcomming_api_key(self) -> "typing.Union[str, None]":
19
22
  if self.outcomming_api_key_env is None:
20
23
  return None
21
24
  value = os.environ.get(self.outcomming_api_key_env, "").strip()
22
25
  return value or None
23
26
 
24
- def outcomming_chat_completions_url(self) -> 'str':
27
+ def outcomming_chat_completions_url(self) -> "str":
25
28
  base = self.outcomming_base_url.rstrip("/")
26
29
  return f"{base}/chat/completions"
27
30
 
28
- def outcomming_messages_url(self) -> 'str':
31
+ def outcomming_messages_url(self) -> "str":
29
32
  base = self.outcomming_base_url.rstrip("/")
30
33
  return f"{base}/messages"
31
34
 
32
- def outcomming_models_url(self) -> 'str':
35
+ def outcomming_models_url(self) -> "str":
33
36
  base = self.outcomming_base_url.rstrip("/")
34
37
  return f"{base}/models"
35
38
 
36
- def normalized_outcomming_api(self) -> 'str':
39
+ def normalized_outcomming_api(self) -> "str":
37
40
  value = str(self.outcomming_api or "").strip().lower()
38
41
  return value or "chat_completions"
39
42
 
40
- def with_ephemeral_port(self) -> 'CompatServerConfig':
43
+ def with_ephemeral_port(self) -> "CompatServerConfig":
41
44
  return CompatServerConfig(
42
45
  host=self.host,
43
46
  port=0,
@@ -51,11 +54,11 @@ class CompatServerConfig:
51
54
  @classmethod
52
55
  def from_base_url(
53
56
  cls,
54
- outcomming_base_url: 'str',
55
- api_key_env: 'typing.Union[str, None]' = None,
56
- model_provider: 'typing.Union[str, None]' = None,
57
- outcomming_api: 'str' = "chat_completions",
58
- ) -> 'CompatServerConfig':
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":
59
62
  parsed = urllib.parse.urlparse(outcomming_base_url)
60
63
  if not parsed.scheme or not parsed.netloc:
61
64
  raise ValueError(f"invalid outcomming base url: {outcomming_base_url}")