mycode-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. mycode/__init__.py +1 -0
  2. mycode/cli/__init__.py +1 -0
  3. mycode/cli/chat.py +650 -0
  4. mycode/cli/main.py +245 -0
  5. mycode/cli/render.py +693 -0
  6. mycode/cli/runtime.py +271 -0
  7. mycode/cli/theme.py +109 -0
  8. mycode/core/__init__.py +30 -0
  9. mycode/core/agent.py +515 -0
  10. mycode/core/config.py +551 -0
  11. mycode/core/messages.py +166 -0
  12. mycode/core/models.py +144 -0
  13. mycode/core/models_catalog.json +2090 -0
  14. mycode/core/providers/__init__.py +86 -0
  15. mycode/core/providers/anthropic_like.py +366 -0
  16. mycode/core/providers/base.py +351 -0
  17. mycode/core/providers/gemini.py +321 -0
  18. mycode/core/providers/openai_chat.py +356 -0
  19. mycode/core/providers/openai_responses.py +326 -0
  20. mycode/core/session.py +537 -0
  21. mycode/core/system_prompt.md +10 -0
  22. mycode/core/system_prompt.py +319 -0
  23. mycode/core/tools.py +898 -0
  24. mycode/server/__init__.py +1 -0
  25. mycode/server/app.py +52 -0
  26. mycode/server/deps.py +29 -0
  27. mycode/server/routers/__init__.py +7 -0
  28. mycode/server/routers/chat.py +320 -0
  29. mycode/server/routers/sessions.py +77 -0
  30. mycode/server/routers/workspaces.py +92 -0
  31. mycode/server/run_manager.py +195 -0
  32. mycode/server/schemas.py +66 -0
  33. mycode/server/static/assets/EditDiff-C1ql7kft.js +12 -0
  34. mycode/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  35. mycode/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  36. mycode/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  37. mycode/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  38. mycode/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  39. mycode/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  40. mycode/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  41. mycode/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  42. mycode/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  43. mycode/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  44. mycode/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  45. mycode/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  46. mycode/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  47. mycode/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  48. mycode/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  49. mycode/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  50. mycode/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  51. mycode/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  52. mycode/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  53. mycode/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  54. mycode/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  55. mycode/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  56. mycode/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  57. mycode/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  58. mycode/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  59. mycode/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  60. mycode/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  61. mycode/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  62. mycode/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  63. mycode/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  64. mycode/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  65. mycode/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  66. mycode/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  67. mycode/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  68. mycode/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  69. mycode/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  70. mycode/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  71. mycode/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  72. mycode/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  73. mycode/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  74. mycode/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  75. mycode/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  76. mycode/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  77. mycode/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  78. mycode/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  79. mycode/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  80. mycode/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  81. mycode/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  82. mycode/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  83. mycode/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  84. mycode/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  85. mycode/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  86. mycode/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  87. mycode/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  88. mycode/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  89. mycode/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  90. mycode/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  91. mycode/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  92. mycode/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  93. mycode/server/static/assets/abap-BdImnpbu.js +1 -0
  94. mycode/server/static/assets/actionscript-3-CoDkCxhg.js +1 -0
  95. mycode/server/static/assets/ada-bCR0ucgS.js +1 -0
  96. mycode/server/static/assets/andromeeda-C4gqWexZ.js +1 -0
  97. mycode/server/static/assets/angular-html-DA-rfuFy.js +1 -0
  98. mycode/server/static/assets/angular-ts-BrjP3tb8.js +1 -0
  99. mycode/server/static/assets/apache-Pmp26Uib.js +1 -0
  100. mycode/server/static/assets/apex-D8_7TLub.js +1 -0
  101. mycode/server/static/assets/apl-CORt7UWP.js +1 -0
  102. mycode/server/static/assets/applescript-Co6uUVPk.js +1 -0
  103. mycode/server/static/assets/ara-BRHolxvo.js +1 -0
  104. mycode/server/static/assets/asciidoc-Ve4PFQV2.js +1 -0
  105. mycode/server/static/assets/asm-D_Q5rh1f.js +1 -0
  106. mycode/server/static/assets/astro-HNnZUWAn.js +1 -0
  107. mycode/server/static/assets/aurora-x-D-2ljcwZ.js +1 -0
  108. mycode/server/static/assets/auto-render-xntwXHOX.js +261 -0
  109. mycode/server/static/assets/awk-DMzUqQB5.js +1 -0
  110. mycode/server/static/assets/ayu-dark-DYE7WIF3.js +1 -0
  111. mycode/server/static/assets/ayu-light-BA47KaF1.js +1 -0
  112. mycode/server/static/assets/ayu-mirage-32ctXXKs.js +1 -0
  113. mycode/server/static/assets/ballerina-BFfxhgS-.js +1 -0
  114. mycode/server/static/assets/bat-BkioyH1T.js +1 -0
  115. mycode/server/static/assets/beancount-k_qm7-4y.js +1 -0
  116. mycode/server/static/assets/berry-uYugtg8r.js +1 -0
  117. mycode/server/static/assets/bibtex-CHM0blh-.js +1 -0
  118. mycode/server/static/assets/bicep-Bmn6On1c.js +1 -0
  119. mycode/server/static/assets/bird2-BIv1doCn.js +1 -0
  120. mycode/server/static/assets/blade-BjGOyj-B.js +1 -0
  121. mycode/server/static/assets/bsl-BO_Y6i37.js +1 -0
  122. mycode/server/static/assets/c-BIGW1oBm.js +1 -0
  123. mycode/server/static/assets/c3-eo99z4R2.js +1 -0
  124. mycode/server/static/assets/cadence-Bv_4Rxtq.js +1 -0
  125. mycode/server/static/assets/cairo-KRGpt6FW.js +1 -0
  126. mycode/server/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  127. mycode/server/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  128. mycode/server/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  129. mycode/server/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  130. mycode/server/static/assets/clarity-D53aC0YG.js +1 -0
  131. mycode/server/static/assets/clojure-P80f7IUj.js +1 -0
  132. mycode/server/static/assets/cmake-D1j8_8rp.js +1 -0
  133. mycode/server/static/assets/cobol-nBiQ_Alo.js +1 -0
  134. mycode/server/static/assets/codeowners-Bp6g37R7.js +1 -0
  135. mycode/server/static/assets/codeql-DsOJ9woJ.js +1 -0
  136. mycode/server/static/assets/coffee-Ch7k5sss.js +1 -0
  137. mycode/server/static/assets/common-lisp-Cg-RD9OK.js +1 -0
  138. mycode/server/static/assets/coq-DkFqJrB1.js +1 -0
  139. mycode/server/static/assets/cpp-CofmeUqb.js +1 -0
  140. mycode/server/static/assets/crystal-DNxU26gB.js +1 -0
  141. mycode/server/static/assets/csharp-COcwbKMJ.js +1 -0
  142. mycode/server/static/assets/css-CLj8gQPS.js +1 -0
  143. mycode/server/static/assets/csv-fuZLfV_i.js +1 -0
  144. mycode/server/static/assets/cue-D82EKSYY.js +1 -0
  145. mycode/server/static/assets/cypher-COkxafJQ.js +1 -0
  146. mycode/server/static/assets/d-85-TOEBH.js +1 -0
  147. mycode/server/static/assets/dark-plus-C3mMm8J8.js +1 -0
  148. mycode/server/static/assets/dart-bE4Kk8sk.js +1 -0
  149. mycode/server/static/assets/dax-CEL-wOlO.js +1 -0
  150. mycode/server/static/assets/desktop-BmXAJ9_W.js +1 -0
  151. mycode/server/static/assets/diff-D97Zzqfu.js +1 -0
  152. mycode/server/static/assets/docker-BcOcwvcX.js +1 -0
  153. mycode/server/static/assets/dotenv-Da5cRb03.js +1 -0
  154. mycode/server/static/assets/dracula-BzJJZx-M.js +1 -0
  155. mycode/server/static/assets/dracula-soft-BXkSAIEj.js +1 -0
  156. mycode/server/static/assets/dream-maker-BtqSS_iP.js +1 -0
  157. mycode/server/static/assets/edge-FbVlp4U3.js +1 -0
  158. mycode/server/static/assets/elixir-CkH2-t6x.js +1 -0
  159. mycode/server/static/assets/elm-DbKCFpqz.js +1 -0
  160. mycode/server/static/assets/emacs-lisp-CXvaQtF9.js +1 -0
  161. mycode/server/static/assets/erb-BYCe7drp.js +1 -0
  162. mycode/server/static/assets/erlang-DsQrWhSR.js +1 -0
  163. mycode/server/static/assets/everforest-dark-BgDCqdQA.js +1 -0
  164. mycode/server/static/assets/everforest-light-C8M2exoo.js +1 -0
  165. mycode/server/static/assets/fennel-BYunw83y.js +1 -0
  166. mycode/server/static/assets/fish-BvzEVeQv.js +1 -0
  167. mycode/server/static/assets/fluent-C4IJs8-o.js +1 -0
  168. mycode/server/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  169. mycode/server/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
  170. mycode/server/static/assets/fsharp-CXgrBDvD.js +1 -0
  171. mycode/server/static/assets/gdresource-BOOCDP_w.js +1 -0
  172. mycode/server/static/assets/gdscript-C5YyOfLZ.js +1 -0
  173. mycode/server/static/assets/gdshader-DkwncUOv.js +1 -0
  174. mycode/server/static/assets/genie-D0YGMca9.js +1 -0
  175. mycode/server/static/assets/gherkin-DyxjwDmM.js +1 -0
  176. mycode/server/static/assets/git-commit-F4YmCXRG.js +1 -0
  177. mycode/server/static/assets/git-rebase-r7XF79zn.js +1 -0
  178. mycode/server/static/assets/github-dark-DHJKELXO.js +1 -0
  179. mycode/server/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
  180. mycode/server/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  181. mycode/server/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  182. mycode/server/static/assets/github-light-DAi9KRSo.js +1 -0
  183. mycode/server/static/assets/github-light-default-D7oLnXFd.js +1 -0
  184. mycode/server/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  185. mycode/server/static/assets/gleam-BspZqrRM.js +1 -0
  186. mycode/server/static/assets/glimmer-js-ByusRIyA.js +1 -0
  187. mycode/server/static/assets/glimmer-ts-BfAWNZQY.js +1 -0
  188. mycode/server/static/assets/glsl-DplSGwfg.js +1 -0
  189. mycode/server/static/assets/gn-n2N0HUVH.js +1 -0
  190. mycode/server/static/assets/gnuplot-DdkO51Og.js +1 -0
  191. mycode/server/static/assets/go-C27-OAKa.js +1 -0
  192. mycode/server/static/assets/graphql-ChdNCCLP.js +1 -0
  193. mycode/server/static/assets/groovy-gcz8RCvz.js +1 -0
  194. mycode/server/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  195. mycode/server/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  196. mycode/server/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  197. mycode/server/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  198. mycode/server/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  199. mycode/server/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  200. mycode/server/static/assets/hack-i7_Ulhet.js +1 -0
  201. mycode/server/static/assets/haml-D5jkg6IW.js +1 -0
  202. mycode/server/static/assets/handlebars-BpdQsYii.js +1 -0
  203. mycode/server/static/assets/haskell-Df6bDoY_.js +1 -0
  204. mycode/server/static/assets/haxe-CzTSHFRz.js +1 -0
  205. mycode/server/static/assets/hcl-BWvSN4gD.js +1 -0
  206. mycode/server/static/assets/hjson-D5-asLiD.js +1 -0
  207. mycode/server/static/assets/hlsl-D3lLCCz7.js +1 -0
  208. mycode/server/static/assets/horizon-BUw7H-hv.js +1 -0
  209. mycode/server/static/assets/horizon-bright-CUuTKBJd.js +1 -0
  210. mycode/server/static/assets/houston-DnULxvSX.js +1 -0
  211. mycode/server/static/assets/html-derivative-DlHx6ybY.js +1 -0
  212. mycode/server/static/assets/html-pp8916En.js +1 -0
  213. mycode/server/static/assets/http-jrhK8wxY.js +1 -0
  214. mycode/server/static/assets/hurl-irOxFIW8.js +1 -0
  215. mycode/server/static/assets/hxml-Bvhsp5Yf.js +1 -0
  216. mycode/server/static/assets/hy-DFXneXwc.js +1 -0
  217. mycode/server/static/assets/imba-DGztddWO.js +1 -0
  218. mycode/server/static/assets/index-B4e4WQPq.css +1 -0
  219. mycode/server/static/assets/index-C2xTNJGd.js +203 -0
  220. mycode/server/static/assets/ini-BEwlwnbL.js +1 -0
  221. mycode/server/static/assets/java-CylS5w8V.js +1 -0
  222. mycode/server/static/assets/javascript-wDzz0qaB.js +1 -0
  223. mycode/server/static/assets/jinja-f2NsQr07.js +1 -0
  224. mycode/server/static/assets/jison-wvAkD_A8.js +1 -0
  225. mycode/server/static/assets/json-Cp-IABpG.js +1 -0
  226. mycode/server/static/assets/json5-C9tS-k6U.js +1 -0
  227. mycode/server/static/assets/jsonc-Des-eS-w.js +1 -0
  228. mycode/server/static/assets/jsonl-DcaNXYhu.js +1 -0
  229. mycode/server/static/assets/jsonnet-DFQXde-d.js +1 -0
  230. mycode/server/static/assets/jssm-C2t-YnRu.js +1 -0
  231. mycode/server/static/assets/jsx-g9-lgVsj.js +1 -0
  232. mycode/server/static/assets/julia-CxzCAyBv.js +1 -0
  233. mycode/server/static/assets/just-VxiPbLrw.js +1 -0
  234. mycode/server/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  235. mycode/server/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  236. mycode/server/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
  237. mycode/server/static/assets/katex-DnJR2-55.css +1 -0
  238. mycode/server/static/assets/kdl-DV7GczEv.js +1 -0
  239. mycode/server/static/assets/kotlin-BdnUsdx6.js +1 -0
  240. mycode/server/static/assets/kusto-wEQ09or8.js +1 -0
  241. mycode/server/static/assets/laserwave-DUszq2jm.js +1 -0
  242. mycode/server/static/assets/latex-CWtU0Tv5.js +1 -0
  243. mycode/server/static/assets/lean-BZvkOJ9d.js +1 -0
  244. mycode/server/static/assets/less-B1dDrJ26.js +1 -0
  245. mycode/server/static/assets/light-plus-B7mTdjB0.js +1 -0
  246. mycode/server/static/assets/liquid-C0sCDyMI.js +1 -0
  247. mycode/server/static/assets/llvm-DjAJT7YJ.js +1 -0
  248. mycode/server/static/assets/log-2UxHyX5q.js +1 -0
  249. mycode/server/static/assets/logo-BtOb2qkB.js +1 -0
  250. mycode/server/static/assets/lua-BaeVxFsk.js +1 -0
  251. mycode/server/static/assets/luau-C-HG3fhB.js +1 -0
  252. mycode/server/static/assets/make-CHLpvVh8.js +1 -0
  253. mycode/server/static/assets/markdown-Cvjx9yec.js +1 -0
  254. mycode/server/static/assets/marko-DjSrsDqO.js +1 -0
  255. mycode/server/static/assets/material-theme-D5KoaKCx.js +1 -0
  256. mycode/server/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
  257. mycode/server/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  258. mycode/server/static/assets/material-theme-ocean-CyktbL80.js +1 -0
  259. mycode/server/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  260. mycode/server/static/assets/matlab-D7o27uSR.js +1 -0
  261. mycode/server/static/assets/mdc-DTYItulj.js +1 -0
  262. mycode/server/static/assets/mdx-Cmh6b_Ma.js +1 -0
  263. mycode/server/static/assets/mermaid-mWjccvbQ.js +1 -0
  264. mycode/server/static/assets/min-dark-CafNBF8u.js +1 -0
  265. mycode/server/static/assets/min-light-CTRr51gU.js +1 -0
  266. mycode/server/static/assets/mipsasm-CKIfxQSi.js +1 -0
  267. mycode/server/static/assets/mojo-rZm6bMo-.js +1 -0
  268. mycode/server/static/assets/monokai-D4h5O-jR.js +1 -0
  269. mycode/server/static/assets/moonbit-_H4v1dQx.js +1 -0
  270. mycode/server/static/assets/move-IF9eRakj.js +1 -0
  271. mycode/server/static/assets/narrat-DRg8JJMk.js +1 -0
  272. mycode/server/static/assets/nextflow-C-mBbutL.js +1 -0
  273. mycode/server/static/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  274. mycode/server/static/assets/nginx-BpAMiNFr.js +1 -0
  275. mycode/server/static/assets/night-owl-C39BiMTA.js +1 -0
  276. mycode/server/static/assets/night-owl-light-CMTm3GFP.js +1 -0
  277. mycode/server/static/assets/nim-BIad80T-.js +1 -0
  278. mycode/server/static/assets/nix-CwoSXNpI.js +1 -0
  279. mycode/server/static/assets/nord-Ddv68eIx.js +1 -0
  280. mycode/server/static/assets/nushell-Cz2AlsmD.js +1 -0
  281. mycode/server/static/assets/objective-c-DXmwc3jG.js +1 -0
  282. mycode/server/static/assets/objective-cpp-CLxacb5B.js +1 -0
  283. mycode/server/static/assets/ocaml-C0hk2d4L.js +1 -0
  284. mycode/server/static/assets/odin-BBf5iR-q.js +1 -0
  285. mycode/server/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  286. mycode/server/static/assets/one-light-C3Wv6jpd.js +1 -0
  287. mycode/server/static/assets/openscad-C4EeE6gA.js +1 -0
  288. mycode/server/static/assets/pascal-D93ZcfNL.js +1 -0
  289. mycode/server/static/assets/perl-NvoQZIq0.js +1 -0
  290. mycode/server/static/assets/php-R6g_5hLQ.js +1 -0
  291. mycode/server/static/assets/pkl-u5AG7uiY.js +1 -0
  292. mycode/server/static/assets/plastic-3e1v2bzS.js +1 -0
  293. mycode/server/static/assets/plsql-ChMvpjG-.js +1 -0
  294. mycode/server/static/assets/po-BTJTHyun.js +1 -0
  295. mycode/server/static/assets/poimandres-CS3Unz2-.js +1 -0
  296. mycode/server/static/assets/polar-C0HS_06l.js +1 -0
  297. mycode/server/static/assets/postcss-CXtECtnM.js +1 -0
  298. mycode/server/static/assets/powerquery-CEu0bR-o.js +1 -0
  299. mycode/server/static/assets/powershell-Dpen1YoG.js +1 -0
  300. mycode/server/static/assets/prisma-Dd19v3D-.js +1 -0
  301. mycode/server/static/assets/prolog-CbFg5uaA.js +1 -0
  302. mycode/server/static/assets/proto-C7zT0LnQ.js +1 -0
  303. mycode/server/static/assets/pug-DKIMFp6K.js +1 -0
  304. mycode/server/static/assets/puppet-BMWR74SV.js +1 -0
  305. mycode/server/static/assets/purescript-CklMAg4u.js +1 -0
  306. mycode/server/static/assets/python-B6aJPvgy.js +1 -0
  307. mycode/server/static/assets/qml-3beO22l8.js +1 -0
  308. mycode/server/static/assets/qmldir-C8lEn-DE.js +1 -0
  309. mycode/server/static/assets/qss-IeuSbFQv.js +1 -0
  310. mycode/server/static/assets/r-Dspwwk_N.js +1 -0
  311. mycode/server/static/assets/racket-BqYA7rlc.js +1 -0
  312. mycode/server/static/assets/raku-DXvB9xmW.js +1 -0
  313. mycode/server/static/assets/razor-BDqjjVU7.js +1 -0
  314. mycode/server/static/assets/red-bN70gL4F.js +1 -0
  315. mycode/server/static/assets/reg-C-SQnVFl.js +1 -0
  316. mycode/server/static/assets/regexp-CDVJQ6XC.js +1 -0
  317. mycode/server/static/assets/rel-C3B-1QV4.js +1 -0
  318. mycode/server/static/assets/riscv-BM1_JUlF.js +1 -0
  319. mycode/server/static/assets/ron-D8l8udqQ.js +1 -0
  320. mycode/server/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  321. mycode/server/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  322. mycode/server/static/assets/rose-pine-qdsjHGoJ.js +1 -0
  323. mycode/server/static/assets/rosmsg-BJDFO7_C.js +1 -0
  324. mycode/server/static/assets/rst-CRjBmOyv.js +1 -0
  325. mycode/server/static/assets/ruby-Wjq7vjNf.js +1 -0
  326. mycode/server/static/assets/rust-B1yitclQ.js +1 -0
  327. mycode/server/static/assets/sas-cz2c8ADy.js +1 -0
  328. mycode/server/static/assets/sass-Cj5Yp3dK.js +1 -0
  329. mycode/server/static/assets/scala-C151Ov-r.js +1 -0
  330. mycode/server/static/assets/scheme-C98Dy4si.js +1 -0
  331. mycode/server/static/assets/scss-D5BDwBP9.js +1 -0
  332. mycode/server/static/assets/sdbl-DVxCFoDh.js +1 -0
  333. mycode/server/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
  334. mycode/server/static/assets/shellscript-Yzrsuije.js +1 -0
  335. mycode/server/static/assets/shellsession-BADoaaVG.js +1 -0
  336. mycode/server/static/assets/slack-dark-BthQWCQV.js +1 -0
  337. mycode/server/static/assets/slack-ochin-DqwNpetd.js +1 -0
  338. mycode/server/static/assets/smalltalk-BERRCDM3.js +1 -0
  339. mycode/server/static/assets/snazzy-light-Bw305WKR.js +1 -0
  340. mycode/server/static/assets/solarized-dark-DXbdFlpD.js +1 -0
  341. mycode/server/static/assets/solarized-light-L9t79GZl.js +1 -0
  342. mycode/server/static/assets/solidity-rGO070M0.js +1 -0
  343. mycode/server/static/assets/soy-8wufbnw4.js +1 -0
  344. mycode/server/static/assets/sparql-rVzFXLq3.js +1 -0
  345. mycode/server/static/assets/splunk-BtCnVYZw.js +1 -0
  346. mycode/server/static/assets/sql-BLtJtn59.js +1 -0
  347. mycode/server/static/assets/ssh-config-_ykCGR6B.js +1 -0
  348. mycode/server/static/assets/stata-BH5u7GGu.js +1 -0
  349. mycode/server/static/assets/stylus-BEDo0Tqx.js +1 -0
  350. mycode/server/static/assets/surrealql-Bq5Q-fJD.js +1 -0
  351. mycode/server/static/assets/svelte-Cy7k_4gC.js +1 -0
  352. mycode/server/static/assets/swift-D82vCrfD.js +1 -0
  353. mycode/server/static/assets/synthwave-84-CbfX1IO0.js +1 -0
  354. mycode/server/static/assets/system-verilog-CnnmHF94.js +1 -0
  355. mycode/server/static/assets/systemd-4A_iFExJ.js +1 -0
  356. mycode/server/static/assets/talonscript-CkByrt1z.js +1 -0
  357. mycode/server/static/assets/tasl-QIJgUcNo.js +1 -0
  358. mycode/server/static/assets/tcl-dwOrl1Do.js +1 -0
  359. mycode/server/static/assets/templ-DhtptRzy.js +1 -0
  360. mycode/server/static/assets/terraform-BETggiCN.js +1 -0
  361. mycode/server/static/assets/tex-idrVyKtj.js +1 -0
  362. mycode/server/static/assets/tokyo-night-hegEt444.js +1 -0
  363. mycode/server/static/assets/toml-vGWfd6FD.js +1 -0
  364. mycode/server/static/assets/ts-tags-DQrlYJgV.js +1 -0
  365. mycode/server/static/assets/tsv-B_m7g4N7.js +1 -0
  366. mycode/server/static/assets/tsx-COt5Ahok.js +1 -0
  367. mycode/server/static/assets/turtle-BsS91CYL.js +1 -0
  368. mycode/server/static/assets/twig-xg9kU7Mw.js +1 -0
  369. mycode/server/static/assets/typescript-BPQ3VLAy.js +1 -0
  370. mycode/server/static/assets/typespec-CAFt9gP4.js +1 -0
  371. mycode/server/static/assets/typst-DHCkPAjA.js +1 -0
  372. mycode/server/static/assets/v-BcVCzyr7.js +1 -0
  373. mycode/server/static/assets/vala-CsfeWuGM.js +1 -0
  374. mycode/server/static/assets/vb-D17OF-Vu.js +1 -0
  375. mycode/server/static/assets/verilog-BQ8w6xss.js +1 -0
  376. mycode/server/static/assets/vesper-DU1UobuO.js +1 -0
  377. mycode/server/static/assets/vhdl-CeAyd5Ju.js +1 -0
  378. mycode/server/static/assets/viml-CJc9bBzg.js +1 -0
  379. mycode/server/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
  380. mycode/server/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
  381. mycode/server/static/assets/vitesse-light-CVO1_9PV.js +1 -0
  382. mycode/server/static/assets/vue-D2xRrEX4.js +1 -0
  383. mycode/server/static/assets/vue-html-AaS7Mt5G.js +1 -0
  384. mycode/server/static/assets/vue-vine-BoDAl6tE.js +1 -0
  385. mycode/server/static/assets/vyper-CDx5xZoG.js +1 -0
  386. mycode/server/static/assets/wasm-CG6Dc4jp.js +1 -0
  387. mycode/server/static/assets/wasm-MzD3tlZU.js +1 -0
  388. mycode/server/static/assets/wenyan-BV7otONQ.js +1 -0
  389. mycode/server/static/assets/wgsl-Dx-B1_4e.js +1 -0
  390. mycode/server/static/assets/wikitext-BhOHFoWU.js +1 -0
  391. mycode/server/static/assets/wit-5i3qLPDT.js +1 -0
  392. mycode/server/static/assets/wolfram-lXgVvXCa.js +1 -0
  393. mycode/server/static/assets/xml-sdJ4AIDG.js +1 -0
  394. mycode/server/static/assets/xsl-CtQFsRM5.js +1 -0
  395. mycode/server/static/assets/yaml-Buea-lGh.js +1 -0
  396. mycode/server/static/assets/zenscript-DVFEvuxE.js +1 -0
  397. mycode/server/static/assets/zig-VOosw3JB.js +1 -0
  398. mycode/server/static/favicon_slashes.svg +12 -0
  399. mycode/server/static/index.html +35 -0
  400. mycode_cli-0.1.0.dist-info/METADATA +186 -0
  401. mycode_cli-0.1.0.dist-info/RECORD +404 -0
  402. mycode_cli-0.1.0.dist-info/WHEEL +4 -0
  403. mycode_cli-0.1.0.dist-info/entry_points.txt +2 -0
  404. mycode_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,356 @@
1
+ """Chat Completions adapters for OpenAI-compatible providers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import AsyncIterator
7
+ from dataclasses import dataclass
8
+ from typing import Any
9
+
10
+ from openai import APIError, AsyncOpenAI
11
+
12
+ from mycode.core.messages import assistant_message, text_block, thinking_block, tool_use_block
13
+ from mycode.core.providers.base import (
14
+ DEFAULT_REQUEST_TIMEOUT,
15
+ ProviderAdapter,
16
+ ProviderRequest,
17
+ ProviderStreamEvent,
18
+ dump_model,
19
+ get_native_meta,
20
+ load_image_block_payload,
21
+ )
22
+ from mycode.core.tools import parse_tool_arguments
23
+
24
+
25
+ @dataclass
26
+ class _ChatToolCallState:
27
+ """Accumulate one streamed tool call from chat-completions deltas."""
28
+
29
+ index: int
30
+ tool_id: str | None = None
31
+ name: str = ""
32
+ arguments_text: str = ""
33
+
34
+
35
+ class OpenAIChatAdapter(ProviderAdapter):
36
+ """Base adapter for Chat Completions style providers."""
37
+
38
+ provider_id = "openai_chat"
39
+ label = "OpenAI Chat Completions"
40
+ default_base_url = "https://api.openai.com/v1"
41
+ env_api_key_names = ("OPENAI_API_KEY",)
42
+ auto_discoverable = False
43
+
44
+ async def stream_turn(self, request: ProviderRequest) -> AsyncIterator[ProviderStreamEvent]:
45
+ api_key = self.require_api_key(request.api_key)
46
+ client = AsyncOpenAI(
47
+ api_key=api_key,
48
+ base_url=self.resolve_base_url(request.api_base),
49
+ timeout=DEFAULT_REQUEST_TIMEOUT,
50
+ )
51
+
52
+ # Keep the streamed turn state local to this adapter so the wire-format
53
+ # mapping stays readable in one file.
54
+ tool_calls: dict[int, _ChatToolCallState] = {}
55
+ text_parts: list[str] = []
56
+ thinking_parts: list[str] = []
57
+ thinking_native_meta: dict[str, Any] = {}
58
+ response_id: str | None = None
59
+ response_model: str | None = None
60
+ finish_reason: str | None = None
61
+ usage: Any = None
62
+
63
+ try:
64
+ stream = await client.chat.completions.create(**self._build_request_payload(request), stream=True)
65
+ async for chunk in stream:
66
+ response_id = response_id or getattr(chunk, "id", None)
67
+ response_model = response_model or getattr(chunk, "model", None)
68
+
69
+ if getattr(chunk, "usage", None) is not None:
70
+ usage = chunk.usage
71
+
72
+ if not chunk.choices:
73
+ continue
74
+
75
+ choice = chunk.choices[0]
76
+ if choice.finish_reason:
77
+ finish_reason = choice.finish_reason
78
+
79
+ delta = choice.delta
80
+ reasoning_delta, reasoning_meta_update = self._extract_reasoning_delta(delta)
81
+ if reasoning_delta:
82
+ thinking_parts.append(reasoning_delta)
83
+ thinking_native_meta.update(reasoning_meta_update)
84
+ yield ProviderStreamEvent("thinking_delta", {"text": reasoning_delta})
85
+
86
+ if delta.content:
87
+ text_parts.append(delta.content)
88
+ yield ProviderStreamEvent("text_delta", {"text": delta.content})
89
+
90
+ for tool_call in delta.tool_calls or []:
91
+ index = tool_call.index or 0
92
+ state = tool_calls.setdefault(index, _ChatToolCallState(index=index))
93
+ if tool_call.id:
94
+ state.tool_id = tool_call.id
95
+ function = tool_call.function
96
+ if function is None:
97
+ continue
98
+ if function.name:
99
+ state.name = function.name
100
+ if function.arguments:
101
+ state.arguments_text += function.arguments
102
+ except APIError as exc:
103
+ raise ValueError(str(exc)) from exc
104
+
105
+ blocks = []
106
+ if thinking_parts:
107
+ blocks.append(
108
+ thinking_block(
109
+ "".join(thinking_parts),
110
+ meta={"native": thinking_native_meta} if thinking_native_meta else None,
111
+ )
112
+ )
113
+ if text_parts:
114
+ blocks.append(text_block("".join(text_parts)))
115
+
116
+ for index in sorted(tool_calls):
117
+ state = tool_calls[index]
118
+ raw_arguments = state.arguments_text
119
+ parsed_arguments = parse_tool_arguments(raw_arguments)
120
+ if isinstance(parsed_arguments, str):
121
+ tool_input = {}
122
+ meta = {"native": {"raw_arguments": raw_arguments}}
123
+ else:
124
+ tool_input = parsed_arguments
125
+ meta = None
126
+
127
+ blocks.append(
128
+ tool_use_block(
129
+ tool_id=state.tool_id or f"tool_call_{index}",
130
+ name=state.name,
131
+ input=tool_input,
132
+ meta=meta,
133
+ )
134
+ )
135
+
136
+ final_message = assistant_message(
137
+ blocks,
138
+ provider=self.provider_id,
139
+ model=response_model or request.model,
140
+ provider_message_id=response_id,
141
+ stop_reason=finish_reason,
142
+ usage=dump_model(usage),
143
+ )
144
+ yield ProviderStreamEvent("message_done", {"message": final_message})
145
+
146
+ def _build_request_payload(self, request: ProviderRequest) -> dict[str, Any]:
147
+ messages = []
148
+ if request.system:
149
+ messages.append({"role": "system", "content": request.system})
150
+ for message in self.prepare_messages(request):
151
+ messages.extend(self._serialize_message(message))
152
+
153
+ payload: dict[str, Any] = {
154
+ "model": request.model,
155
+ "messages": messages,
156
+ "tools": [self._serialize_tool(tool) for tool in request.tools] or None,
157
+ "tool_choice": "auto" if request.tools else None,
158
+ "max_tokens": request.max_tokens,
159
+ "stream_options": {"include_usage": True},
160
+ }
161
+ payload.update(self._build_provider_payload_overrides(request))
162
+ return {key: value for key, value in payload.items() if value is not None}
163
+
164
+ def _build_provider_payload_overrides(self, request: ProviderRequest) -> dict[str, Any]:
165
+ return {}
166
+
167
+ def _serialize_tool(self, tool: dict[str, Any]) -> dict[str, Any]:
168
+ return {
169
+ "type": "function",
170
+ "function": {
171
+ "name": tool.get("name") or "",
172
+ "description": tool.get("description") or "",
173
+ "parameters": tool.get("input_schema") or {"type": "object", "properties": {}},
174
+ },
175
+ }
176
+
177
+ def _serialize_message(self, message: dict[str, Any]) -> list[dict[str, Any]]:
178
+ """Convert one canonical message into Chat Completions wire messages."""
179
+
180
+ role = str(message.get("role") or "user")
181
+ blocks = [block for block in message.get("content") or [] if isinstance(block, dict)]
182
+
183
+ if role == "user":
184
+ payload_messages: list[dict[str, Any]] = []
185
+ has_images = any(block.get("type") == "image" for block in blocks)
186
+ if has_images:
187
+ user_content: str | list[dict[str, Any]] | None = []
188
+ for block in blocks:
189
+ block_type = block.get("type")
190
+ if block_type == "text":
191
+ text = str(block.get("text") or "")
192
+ if text:
193
+ user_content.append({"type": "text", "text": text})
194
+ continue
195
+ if block_type != "image":
196
+ continue
197
+
198
+ mime_type, data = load_image_block_payload(block)
199
+ user_content.append(
200
+ {
201
+ "type": "image_url",
202
+ "image_url": {"url": f"data:{mime_type};base64,{data}"},
203
+ }
204
+ )
205
+ if not user_content:
206
+ user_content = None
207
+ else:
208
+ text_parts = [
209
+ str(block.get("text") or "")
210
+ for block in blocks
211
+ if block.get("type") == "text" and block.get("text")
212
+ ]
213
+ text = "\n".join(text_parts)
214
+ user_content = text or None
215
+
216
+ if user_content:
217
+ payload_messages.append({"role": "user", "content": user_content})
218
+
219
+ for block in blocks:
220
+ if block.get("type") != "tool_result":
221
+ continue
222
+ payload_messages.append(
223
+ {
224
+ "role": "tool",
225
+ "tool_call_id": block.get("tool_use_id") or "",
226
+ "content": str(block.get("model_text") or ""),
227
+ }
228
+ )
229
+ return payload_messages
230
+
231
+ if role != "assistant":
232
+ return []
233
+
234
+ text_parts = [str(block.get("text") or "") for block in blocks if block.get("type") == "text"]
235
+ thinking_blocks = [block for block in blocks if block.get("type") == "thinking"]
236
+ tool_use_blocks = [block for block in blocks if block.get("type") == "tool_use"]
237
+
238
+ payload: dict[str, Any] = {
239
+ "role": "assistant",
240
+ "content": "\n".join(part for part in text_parts if part),
241
+ }
242
+
243
+ if tool_use_blocks:
244
+ payload["tool_calls"] = [
245
+ {
246
+ "id": block.get("id") or "",
247
+ "type": "function",
248
+ "function": {
249
+ "name": block.get("name") or "",
250
+ "arguments": json.dumps(
251
+ block.get("input") if isinstance(block.get("input"), dict) else {},
252
+ ensure_ascii=False,
253
+ ),
254
+ },
255
+ }
256
+ for block in tool_use_blocks
257
+ ]
258
+
259
+ if thinking_blocks:
260
+ payload.update(self._serialize_reasoning(thinking_blocks))
261
+
262
+ return [payload]
263
+
264
+ def _serialize_reasoning(self, thinking_blocks: list[dict[str, Any]]) -> dict[str, Any]:
265
+ """Replay canonical thinking through the provider's reasoning field.
266
+
267
+ When the source provider did not record a native field name, default to
268
+ `reasoning_content`, which is the common reasoning slot used by the
269
+ OpenAI-compatible thinking providers we support.
270
+ """
271
+
272
+ thinking_text = "\n".join(str(block.get("text") or "") for block in thinking_blocks if block.get("text"))
273
+ native_meta = get_native_meta(thinking_blocks[0])
274
+ reasoning_field = str(native_meta.get("reasoning_field") or "")
275
+ if reasoning_field == "reasoning_details":
276
+ return {"reasoning_details": native_meta.get("reasoning_details") or []}
277
+ return {"reasoning_content": thinking_text} if thinking_text else {}
278
+
279
+ def _extract_reasoning_delta(self, delta: Any) -> tuple[str, dict[str, Any]]:
280
+ # Third-party providers surface reasoning through non-standard extras.
281
+ # We check both the delta root and model_extra to cover both patterns.
282
+ # Known fields: reasoning_content (Moonshot/MiniMax chat), reasoning_details (some others).
283
+ for source in (delta, getattr(delta, "model_extra", None) or {}):
284
+ if isinstance(source, dict):
285
+ reasoning_content = source.get("reasoning_content")
286
+ reasoning_details = source.get("reasoning_details")
287
+ else:
288
+ reasoning_content = getattr(source, "reasoning_content", None)
289
+ reasoning_details = getattr(source, "reasoning_details", None)
290
+
291
+ if isinstance(reasoning_content, str) and reasoning_content:
292
+ return reasoning_content, {"reasoning_field": "reasoning_content"}
293
+
294
+ if isinstance(reasoning_details, list) and reasoning_details:
295
+ reasoning_text = "".join(
296
+ str(item.get("text") or "") for item in reasoning_details if isinstance(item, dict)
297
+ )
298
+ if reasoning_text:
299
+ return reasoning_text, {
300
+ "reasoning_field": "reasoning_details",
301
+ "reasoning_details": reasoning_details,
302
+ }
303
+
304
+ return "", {}
305
+
306
+
307
+ class DeepSeekAdapter(OpenAIChatAdapter):
308
+ """DeepSeek's OpenAI-compatible chat endpoint.
309
+
310
+ deepseek-reasoner always thinks — no parameter needed to enable it.
311
+ deepseek-chat does not think by default; send thinking: {"type": "enabled"}
312
+ to activate it. We rely on the model's default behavior, so no overrides here.
313
+ """
314
+
315
+ provider_id = "deepseek"
316
+ label = "DeepSeek"
317
+ default_base_url = "https://api.deepseek.com"
318
+ env_api_key_names = ("DEEPSEEK_API_KEY",)
319
+ default_models = ("deepseek-chat", "deepseek-reasoner")
320
+ auto_discoverable = True
321
+
322
+
323
+ class ZAIAdapter(OpenAIChatAdapter):
324
+ """Z.AI's OpenAI-compatible chat endpoint.
325
+
326
+ GLM models think by default. We still send the explicit thinking parameter
327
+ so that clear_thinking=False preserves reasoning across multi-turn tool loops
328
+ instead of resetting it on each turn.
329
+ """
330
+
331
+ provider_id = "zai"
332
+ label = "Z.AI"
333
+ default_base_url = "https://api.z.ai/api/paas/v4/"
334
+ env_api_key_names = ("ZAI_API_KEY",)
335
+ default_models = ("glm-5.1", "glm-5-turbo")
336
+ auto_discoverable = True
337
+
338
+ def _build_provider_payload_overrides(self, request: ProviderRequest) -> dict[str, Any]:
339
+ return {"extra_body": {"thinking": {"type": "enabled", "clear_thinking": False}}}
340
+
341
+
342
+ class OpenRouterAdapter(OpenAIChatAdapter):
343
+ """OpenRouter's OpenAI-compatible chat endpoint."""
344
+
345
+ provider_id = "openrouter"
346
+ label = "OpenRouter"
347
+ default_base_url = "https://openrouter.ai/api/v1"
348
+ env_api_key_names = ("OPENROUTER_API_KEY",)
349
+ default_models = ("openrouter/auto",)
350
+ auto_discoverable = True
351
+ supports_reasoning_effort = True
352
+
353
+ def _build_provider_payload_overrides(self, request: ProviderRequest) -> dict[str, Any]:
354
+ if not request.reasoning_effort:
355
+ return {}
356
+ return {"extra_body": {"reasoning": {"effort": request.reasoning_effort}}}
@@ -0,0 +1,326 @@
1
+ """OpenAI Responses API adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import AsyncIterator
7
+ from copy import deepcopy
8
+ from typing import Any, cast
9
+
10
+ from openai import APIError, AsyncOpenAI
11
+
12
+ from mycode.core.messages import ConversationMessage, assistant_message, text_block, thinking_block, tool_use_block
13
+ from mycode.core.providers.base import (
14
+ DEFAULT_REQUEST_TIMEOUT,
15
+ ProviderAdapter,
16
+ ProviderRequest,
17
+ ProviderStreamEvent,
18
+ dump_model,
19
+ load_image_block_payload,
20
+ tool_result_content_blocks,
21
+ )
22
+ from mycode.core.tools import parse_tool_arguments
23
+
24
+
25
+ class OpenAIResponsesAdapter(ProviderAdapter):
26
+ """Adapter for OpenAI's Responses API."""
27
+
28
+ provider_id = "openai"
29
+ label = "OpenAI Responses"
30
+ default_base_url = "https://api.openai.com/v1"
31
+ env_api_key_names = ("OPENAI_API_KEY",)
32
+ default_models = ("gpt-5.4", "gpt-5.4-mini")
33
+ supports_reasoning_effort = True
34
+
35
+ async def stream_turn(self, request: ProviderRequest) -> AsyncIterator[ProviderStreamEvent]:
36
+ api_key = self.require_api_key(request.api_key)
37
+ client = AsyncOpenAI(
38
+ api_key=api_key,
39
+ base_url=self.resolve_base_url(request.api_base),
40
+ timeout=DEFAULT_REQUEST_TIMEOUT,
41
+ )
42
+
43
+ payload = self._build_request_payload(request)
44
+ try:
45
+ stream = await client.responses.create(**payload, stream=True)
46
+ final_response = None
47
+ async for event in stream:
48
+ if event.type == "response.reasoning_text.delta" and event.delta:
49
+ yield ProviderStreamEvent("thinking_delta", {"text": event.delta})
50
+ continue
51
+
52
+ if event.type == "response.output_text.delta" and event.delta:
53
+ yield ProviderStreamEvent("text_delta", {"text": event.delta})
54
+ continue
55
+
56
+ if event.type == "response.error":
57
+ raise ValueError(str(getattr(event, "error", None) or event))
58
+
59
+ if event.type == "response.failed":
60
+ raise ValueError(str(getattr(event, "response", None) or event))
61
+
62
+ if event.type == "response.completed":
63
+ final_response = event.response
64
+ except APIError as exc:
65
+ raise ValueError(str(exc)) from exc
66
+
67
+ if final_response is None:
68
+ raise ValueError("OpenAI Responses stream ended before response.completed")
69
+
70
+ yield ProviderStreamEvent("message_done", {"message": self._convert_final_response(final_response)})
71
+
72
+ def _build_request_payload(self, request: ProviderRequest) -> dict[str, Any]:
73
+ prepared_messages = self.prepare_messages(request)
74
+ input_items: list[dict[str, Any]] = []
75
+ for message in prepared_messages:
76
+ role = message.get("role")
77
+ if role == "user":
78
+ input_items.extend(self._serialize_user_message(message))
79
+ continue
80
+
81
+ if role != "assistant":
82
+ continue
83
+
84
+ native_output_items = self._native_output_items(message)
85
+ if native_output_items is not None:
86
+ input_items.extend(native_output_items)
87
+ continue
88
+
89
+ input_items.extend(self._serialize_fallback_assistant_message(message))
90
+
91
+ payload: dict[str, Any] = {
92
+ "model": request.model,
93
+ "input": input_items,
94
+ "instructions": request.system or None,
95
+ "store": False,
96
+ "include": ["reasoning.encrypted_content"],
97
+ "prompt_cache_key": request.session_id or None,
98
+ "max_output_tokens": request.max_tokens,
99
+ "tools": [self._serialize_tool(tool) for tool in request.tools] or None,
100
+ "tool_choice": "auto" if request.tools else None,
101
+ }
102
+ if request.reasoning_effort:
103
+ payload["reasoning"] = {"effort": request.reasoning_effort}
104
+ return {key: value for key, value in payload.items() if value is not None}
105
+
106
+ def _serialize_user_message(self, message: ConversationMessage) -> list[dict[str, Any]]:
107
+ items: list[dict[str, Any]] = []
108
+ blocks = [block for block in message.get("content") or [] if isinstance(block, dict)]
109
+ message_content = self._serialize_input_content(
110
+ [block for block in blocks if block.get("type") in {"text", "image"}]
111
+ )
112
+ if message_content:
113
+ items.append(
114
+ {
115
+ "type": "message",
116
+ "role": "user",
117
+ "content": message_content,
118
+ }
119
+ )
120
+
121
+ for block in blocks:
122
+ if block.get("type") != "tool_result":
123
+ continue
124
+ result_blocks = tool_result_content_blocks(block)
125
+ has_images = any(item.get("type") == "image" for item in result_blocks)
126
+ if has_images:
127
+ output: str | list[dict[str, Any]] = self._serialize_input_content(result_blocks)
128
+ else:
129
+ output = str(block.get("model_text") or "")
130
+ items.append(
131
+ {
132
+ "type": "function_call_output",
133
+ "call_id": block.get("tool_use_id") or "",
134
+ "output": output,
135
+ }
136
+ )
137
+
138
+ return items
139
+
140
+ def _serialize_input_content(self, blocks: list[dict[str, Any]]) -> list[dict[str, Any]]:
141
+ content: list[dict[str, Any]] = []
142
+ for block in blocks:
143
+ block_type = block.get("type")
144
+ if block_type == "text":
145
+ content.append({"type": "input_text", "text": str(block.get("text") or "")})
146
+ continue
147
+ if block_type == "image":
148
+ mime_type, data = load_image_block_payload(block)
149
+ content.append({"type": "input_image", "image_url": f"data:{mime_type};base64,{data}"})
150
+ return content
151
+
152
+ def _native_output_items(self, message: ConversationMessage) -> list[dict[str, Any]] | None:
153
+ """Replay stored OpenAI output items when history already came from Responses."""
154
+
155
+ raw_meta = message.get("meta")
156
+ if not isinstance(raw_meta, dict) or raw_meta.get("provider") != self.provider_id:
157
+ return None
158
+
159
+ native_meta = raw_meta.get("native")
160
+ output_items = native_meta.get("output_items") if isinstance(native_meta, dict) else None
161
+ if not isinstance(output_items, list) or not output_items:
162
+ return None
163
+
164
+ replay_items: list[dict[str, Any]] = []
165
+ for item in cast(list[dict[str, Any]], deepcopy(output_items)):
166
+ item_type = str(item.get("type") or "")
167
+ item.pop("status", None)
168
+ if item_type != "reasoning":
169
+ item.pop("id", None)
170
+ replay_items.append(item)
171
+
172
+ return replay_items
173
+
174
+ def _serialize_fallback_assistant_message(self, message: ConversationMessage) -> list[dict[str, Any]]:
175
+ blocks = [block for block in message.get("content") or [] if isinstance(block, dict)]
176
+ text_parts = [
177
+ str(block.get("text") or "") for block in blocks if block.get("type") == "text" and block.get("text")
178
+ ]
179
+
180
+ items: list[dict[str, Any]] = []
181
+ if text_parts:
182
+ message_item: dict[str, Any] = {
183
+ "type": "message",
184
+ "role": "assistant",
185
+ "content": [{"type": "output_text", "text": "\n".join(text_parts)}],
186
+ }
187
+ items.append(message_item)
188
+
189
+ for block in blocks:
190
+ if block.get("type") != "tool_use":
191
+ continue
192
+ call_item: dict[str, Any] = {
193
+ "type": "function_call",
194
+ "call_id": block.get("id") or "",
195
+ "name": block.get("name") or "",
196
+ "arguments": json.dumps(block.get("input") if isinstance(block.get("input"), dict) else {}),
197
+ }
198
+ items.append(call_item)
199
+
200
+ return items
201
+
202
+ def _serialize_tool(self, tool: dict[str, Any]) -> dict[str, Any]:
203
+ parameters = cast(dict[str, Any], dict(tool.get("input_schema") or {"type": "object", "properties": {}}))
204
+ properties = parameters.get("properties")
205
+ required = parameters.get("required")
206
+
207
+ # OpenAI strict tools require every top-level property to appear in
208
+ # `required`. Our built-in tool schemas are flat, so optional fields only
209
+ # need a shallow nullable conversion here.
210
+ if isinstance(properties, dict):
211
+ copied_properties: dict[str, Any] = {
212
+ key: dict(value) if isinstance(value, dict) else value for key, value in properties.items()
213
+ }
214
+ required_names = {str(name) for name in required} if isinstance(required, list) else set()
215
+
216
+ for name, property_schema in copied_properties.items():
217
+ if name in required_names or not isinstance(property_schema, dict):
218
+ continue
219
+
220
+ property_type = property_schema.get("type")
221
+ if isinstance(property_type, str):
222
+ property_schema["type"] = [property_type, "null"]
223
+ elif isinstance(property_type, list):
224
+ if "null" not in property_type:
225
+ property_schema["type"] = [*property_type, "null"]
226
+ else:
227
+ copied_properties[name] = {"anyOf": [property_schema, {"type": "null"}]}
228
+ continue
229
+
230
+ enum_values = property_schema.get("enum")
231
+ if isinstance(enum_values, list) and None not in enum_values:
232
+ property_schema["enum"] = [*enum_values, None]
233
+
234
+ parameters["properties"] = copied_properties
235
+ parameters["required"] = list(copied_properties.keys())
236
+
237
+ return {
238
+ "type": "function",
239
+ "name": tool.get("name") or "",
240
+ "description": tool.get("description") or "",
241
+ "parameters": parameters,
242
+ "strict": True,
243
+ }
244
+
245
+ def _convert_final_response(self, response: Any) -> dict[str, Any]:
246
+ output_items = dump_model(getattr(response, "output", None))
247
+ blocks: list[dict[str, Any]] = []
248
+ for item in getattr(response, "output", []) or []:
249
+ item_type = getattr(item, "type", None)
250
+
251
+ if item_type == "reasoning":
252
+ text_parts = []
253
+ for content in getattr(item, "content", None) or []:
254
+ text = getattr(content, "text", None)
255
+ if text:
256
+ text_parts.append(text)
257
+
258
+ if not text_parts:
259
+ for summary in getattr(item, "summary", None) or []:
260
+ text = getattr(summary, "text", None)
261
+ if text:
262
+ text_parts.append(text)
263
+
264
+ native_meta = {
265
+ "item_id": getattr(item, "id", None),
266
+ "status": getattr(item, "status", None),
267
+ }
268
+ summary = dump_model(getattr(item, "summary", None))
269
+ if summary:
270
+ native_meta["summary"] = summary
271
+ filtered_native_meta = {key: value for key, value in native_meta.items() if value is not None}
272
+ blocks.append(
273
+ thinking_block(
274
+ "".join(text_parts),
275
+ meta={"native": filtered_native_meta} if filtered_native_meta else None,
276
+ )
277
+ )
278
+ continue
279
+
280
+ if item_type == "message":
281
+ for part in getattr(item, "content", []) or []:
282
+ if getattr(part, "type", None) != "output_text":
283
+ continue
284
+ native_meta = {}
285
+ annotations = dump_model(getattr(part, "annotations", None))
286
+ if annotations:
287
+ native_meta["annotations"] = annotations
288
+ blocks.append(
289
+ text_block(
290
+ getattr(part, "text", ""),
291
+ meta={"native": native_meta} if native_meta else None,
292
+ )
293
+ )
294
+ continue
295
+
296
+ if item_type == "function_call":
297
+ raw_arguments = getattr(item, "arguments", "") or ""
298
+ parsed_arguments = parse_tool_arguments(raw_arguments)
299
+ native_meta = {
300
+ "item_id": getattr(item, "id", None),
301
+ "status": getattr(item, "status", None),
302
+ }
303
+ if isinstance(parsed_arguments, str):
304
+ tool_input = {}
305
+ native_meta["raw_arguments"] = raw_arguments
306
+ else:
307
+ tool_input = parsed_arguments
308
+ filtered_native_meta = {key: value for key, value in native_meta.items() if value is not None}
309
+ blocks.append(
310
+ tool_use_block(
311
+ tool_id=getattr(item, "call_id", ""),
312
+ name=getattr(item, "name", ""),
313
+ input=tool_input,
314
+ meta={"native": filtered_native_meta} if filtered_native_meta else None,
315
+ )
316
+ )
317
+
318
+ return assistant_message(
319
+ blocks,
320
+ provider=self.provider_id,
321
+ model=getattr(response, "model", None),
322
+ provider_message_id=getattr(response, "id", None),
323
+ stop_reason=getattr(response, "status", None),
324
+ usage=dump_model(getattr(response, "usage", None)),
325
+ native_meta={"output_items": output_items} if output_items else None,
326
+ )