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.
- mycode/__init__.py +1 -0
- mycode/cli/__init__.py +1 -0
- mycode/cli/chat.py +650 -0
- mycode/cli/main.py +245 -0
- mycode/cli/render.py +693 -0
- mycode/cli/runtime.py +271 -0
- mycode/cli/theme.py +109 -0
- mycode/core/__init__.py +30 -0
- mycode/core/agent.py +515 -0
- mycode/core/config.py +551 -0
- mycode/core/messages.py +166 -0
- mycode/core/models.py +144 -0
- mycode/core/models_catalog.json +2090 -0
- mycode/core/providers/__init__.py +86 -0
- mycode/core/providers/anthropic_like.py +366 -0
- mycode/core/providers/base.py +351 -0
- mycode/core/providers/gemini.py +321 -0
- mycode/core/providers/openai_chat.py +356 -0
- mycode/core/providers/openai_responses.py +326 -0
- mycode/core/session.py +537 -0
- mycode/core/system_prompt.md +10 -0
- mycode/core/system_prompt.py +319 -0
- mycode/core/tools.py +898 -0
- mycode/server/__init__.py +1 -0
- mycode/server/app.py +52 -0
- mycode/server/deps.py +29 -0
- mycode/server/routers/__init__.py +7 -0
- mycode/server/routers/chat.py +320 -0
- mycode/server/routers/sessions.py +77 -0
- mycode/server/routers/workspaces.py +92 -0
- mycode/server/run_manager.py +195 -0
- mycode/server/schemas.py +66 -0
- mycode/server/static/assets/EditDiff-C1ql7kft.js +12 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- mycode/server/static/assets/abap-BdImnpbu.js +1 -0
- mycode/server/static/assets/actionscript-3-CoDkCxhg.js +1 -0
- mycode/server/static/assets/ada-bCR0ucgS.js +1 -0
- mycode/server/static/assets/andromeeda-C4gqWexZ.js +1 -0
- mycode/server/static/assets/angular-html-DA-rfuFy.js +1 -0
- mycode/server/static/assets/angular-ts-BrjP3tb8.js +1 -0
- mycode/server/static/assets/apache-Pmp26Uib.js +1 -0
- mycode/server/static/assets/apex-D8_7TLub.js +1 -0
- mycode/server/static/assets/apl-CORt7UWP.js +1 -0
- mycode/server/static/assets/applescript-Co6uUVPk.js +1 -0
- mycode/server/static/assets/ara-BRHolxvo.js +1 -0
- mycode/server/static/assets/asciidoc-Ve4PFQV2.js +1 -0
- mycode/server/static/assets/asm-D_Q5rh1f.js +1 -0
- mycode/server/static/assets/astro-HNnZUWAn.js +1 -0
- mycode/server/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- mycode/server/static/assets/auto-render-xntwXHOX.js +261 -0
- mycode/server/static/assets/awk-DMzUqQB5.js +1 -0
- mycode/server/static/assets/ayu-dark-DYE7WIF3.js +1 -0
- mycode/server/static/assets/ayu-light-BA47KaF1.js +1 -0
- mycode/server/static/assets/ayu-mirage-32ctXXKs.js +1 -0
- mycode/server/static/assets/ballerina-BFfxhgS-.js +1 -0
- mycode/server/static/assets/bat-BkioyH1T.js +1 -0
- mycode/server/static/assets/beancount-k_qm7-4y.js +1 -0
- mycode/server/static/assets/berry-uYugtg8r.js +1 -0
- mycode/server/static/assets/bibtex-CHM0blh-.js +1 -0
- mycode/server/static/assets/bicep-Bmn6On1c.js +1 -0
- mycode/server/static/assets/bird2-BIv1doCn.js +1 -0
- mycode/server/static/assets/blade-BjGOyj-B.js +1 -0
- mycode/server/static/assets/bsl-BO_Y6i37.js +1 -0
- mycode/server/static/assets/c-BIGW1oBm.js +1 -0
- mycode/server/static/assets/c3-eo99z4R2.js +1 -0
- mycode/server/static/assets/cadence-Bv_4Rxtq.js +1 -0
- mycode/server/static/assets/cairo-KRGpt6FW.js +1 -0
- mycode/server/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- mycode/server/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- mycode/server/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- mycode/server/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- mycode/server/static/assets/clarity-D53aC0YG.js +1 -0
- mycode/server/static/assets/clojure-P80f7IUj.js +1 -0
- mycode/server/static/assets/cmake-D1j8_8rp.js +1 -0
- mycode/server/static/assets/cobol-nBiQ_Alo.js +1 -0
- mycode/server/static/assets/codeowners-Bp6g37R7.js +1 -0
- mycode/server/static/assets/codeql-DsOJ9woJ.js +1 -0
- mycode/server/static/assets/coffee-Ch7k5sss.js +1 -0
- mycode/server/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- mycode/server/static/assets/coq-DkFqJrB1.js +1 -0
- mycode/server/static/assets/cpp-CofmeUqb.js +1 -0
- mycode/server/static/assets/crystal-DNxU26gB.js +1 -0
- mycode/server/static/assets/csharp-COcwbKMJ.js +1 -0
- mycode/server/static/assets/css-CLj8gQPS.js +1 -0
- mycode/server/static/assets/csv-fuZLfV_i.js +1 -0
- mycode/server/static/assets/cue-D82EKSYY.js +1 -0
- mycode/server/static/assets/cypher-COkxafJQ.js +1 -0
- mycode/server/static/assets/d-85-TOEBH.js +1 -0
- mycode/server/static/assets/dark-plus-C3mMm8J8.js +1 -0
- mycode/server/static/assets/dart-bE4Kk8sk.js +1 -0
- mycode/server/static/assets/dax-CEL-wOlO.js +1 -0
- mycode/server/static/assets/desktop-BmXAJ9_W.js +1 -0
- mycode/server/static/assets/diff-D97Zzqfu.js +1 -0
- mycode/server/static/assets/docker-BcOcwvcX.js +1 -0
- mycode/server/static/assets/dotenv-Da5cRb03.js +1 -0
- mycode/server/static/assets/dracula-BzJJZx-M.js +1 -0
- mycode/server/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- mycode/server/static/assets/dream-maker-BtqSS_iP.js +1 -0
- mycode/server/static/assets/edge-FbVlp4U3.js +1 -0
- mycode/server/static/assets/elixir-CkH2-t6x.js +1 -0
- mycode/server/static/assets/elm-DbKCFpqz.js +1 -0
- mycode/server/static/assets/emacs-lisp-CXvaQtF9.js +1 -0
- mycode/server/static/assets/erb-BYCe7drp.js +1 -0
- mycode/server/static/assets/erlang-DsQrWhSR.js +1 -0
- mycode/server/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- mycode/server/static/assets/everforest-light-C8M2exoo.js +1 -0
- mycode/server/static/assets/fennel-BYunw83y.js +1 -0
- mycode/server/static/assets/fish-BvzEVeQv.js +1 -0
- mycode/server/static/assets/fluent-C4IJs8-o.js +1 -0
- mycode/server/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- mycode/server/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- mycode/server/static/assets/fsharp-CXgrBDvD.js +1 -0
- mycode/server/static/assets/gdresource-BOOCDP_w.js +1 -0
- mycode/server/static/assets/gdscript-C5YyOfLZ.js +1 -0
- mycode/server/static/assets/gdshader-DkwncUOv.js +1 -0
- mycode/server/static/assets/genie-D0YGMca9.js +1 -0
- mycode/server/static/assets/gherkin-DyxjwDmM.js +1 -0
- mycode/server/static/assets/git-commit-F4YmCXRG.js +1 -0
- mycode/server/static/assets/git-rebase-r7XF79zn.js +1 -0
- mycode/server/static/assets/github-dark-DHJKELXO.js +1 -0
- mycode/server/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- mycode/server/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- mycode/server/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- mycode/server/static/assets/github-light-DAi9KRSo.js +1 -0
- mycode/server/static/assets/github-light-default-D7oLnXFd.js +1 -0
- mycode/server/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- mycode/server/static/assets/gleam-BspZqrRM.js +1 -0
- mycode/server/static/assets/glimmer-js-ByusRIyA.js +1 -0
- mycode/server/static/assets/glimmer-ts-BfAWNZQY.js +1 -0
- mycode/server/static/assets/glsl-DplSGwfg.js +1 -0
- mycode/server/static/assets/gn-n2N0HUVH.js +1 -0
- mycode/server/static/assets/gnuplot-DdkO51Og.js +1 -0
- mycode/server/static/assets/go-C27-OAKa.js +1 -0
- mycode/server/static/assets/graphql-ChdNCCLP.js +1 -0
- mycode/server/static/assets/groovy-gcz8RCvz.js +1 -0
- mycode/server/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- mycode/server/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- mycode/server/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- mycode/server/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- mycode/server/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- mycode/server/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- mycode/server/static/assets/hack-i7_Ulhet.js +1 -0
- mycode/server/static/assets/haml-D5jkg6IW.js +1 -0
- mycode/server/static/assets/handlebars-BpdQsYii.js +1 -0
- mycode/server/static/assets/haskell-Df6bDoY_.js +1 -0
- mycode/server/static/assets/haxe-CzTSHFRz.js +1 -0
- mycode/server/static/assets/hcl-BWvSN4gD.js +1 -0
- mycode/server/static/assets/hjson-D5-asLiD.js +1 -0
- mycode/server/static/assets/hlsl-D3lLCCz7.js +1 -0
- mycode/server/static/assets/horizon-BUw7H-hv.js +1 -0
- mycode/server/static/assets/horizon-bright-CUuTKBJd.js +1 -0
- mycode/server/static/assets/houston-DnULxvSX.js +1 -0
- mycode/server/static/assets/html-derivative-DlHx6ybY.js +1 -0
- mycode/server/static/assets/html-pp8916En.js +1 -0
- mycode/server/static/assets/http-jrhK8wxY.js +1 -0
- mycode/server/static/assets/hurl-irOxFIW8.js +1 -0
- mycode/server/static/assets/hxml-Bvhsp5Yf.js +1 -0
- mycode/server/static/assets/hy-DFXneXwc.js +1 -0
- mycode/server/static/assets/imba-DGztddWO.js +1 -0
- mycode/server/static/assets/index-B4e4WQPq.css +1 -0
- mycode/server/static/assets/index-C2xTNJGd.js +203 -0
- mycode/server/static/assets/ini-BEwlwnbL.js +1 -0
- mycode/server/static/assets/java-CylS5w8V.js +1 -0
- mycode/server/static/assets/javascript-wDzz0qaB.js +1 -0
- mycode/server/static/assets/jinja-f2NsQr07.js +1 -0
- mycode/server/static/assets/jison-wvAkD_A8.js +1 -0
- mycode/server/static/assets/json-Cp-IABpG.js +1 -0
- mycode/server/static/assets/json5-C9tS-k6U.js +1 -0
- mycode/server/static/assets/jsonc-Des-eS-w.js +1 -0
- mycode/server/static/assets/jsonl-DcaNXYhu.js +1 -0
- mycode/server/static/assets/jsonnet-DFQXde-d.js +1 -0
- mycode/server/static/assets/jssm-C2t-YnRu.js +1 -0
- mycode/server/static/assets/jsx-g9-lgVsj.js +1 -0
- mycode/server/static/assets/julia-CxzCAyBv.js +1 -0
- mycode/server/static/assets/just-VxiPbLrw.js +1 -0
- mycode/server/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- mycode/server/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- mycode/server/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- mycode/server/static/assets/katex-DnJR2-55.css +1 -0
- mycode/server/static/assets/kdl-DV7GczEv.js +1 -0
- mycode/server/static/assets/kotlin-BdnUsdx6.js +1 -0
- mycode/server/static/assets/kusto-wEQ09or8.js +1 -0
- mycode/server/static/assets/laserwave-DUszq2jm.js +1 -0
- mycode/server/static/assets/latex-CWtU0Tv5.js +1 -0
- mycode/server/static/assets/lean-BZvkOJ9d.js +1 -0
- mycode/server/static/assets/less-B1dDrJ26.js +1 -0
- mycode/server/static/assets/light-plus-B7mTdjB0.js +1 -0
- mycode/server/static/assets/liquid-C0sCDyMI.js +1 -0
- mycode/server/static/assets/llvm-DjAJT7YJ.js +1 -0
- mycode/server/static/assets/log-2UxHyX5q.js +1 -0
- mycode/server/static/assets/logo-BtOb2qkB.js +1 -0
- mycode/server/static/assets/lua-BaeVxFsk.js +1 -0
- mycode/server/static/assets/luau-C-HG3fhB.js +1 -0
- mycode/server/static/assets/make-CHLpvVh8.js +1 -0
- mycode/server/static/assets/markdown-Cvjx9yec.js +1 -0
- mycode/server/static/assets/marko-DjSrsDqO.js +1 -0
- mycode/server/static/assets/material-theme-D5KoaKCx.js +1 -0
- mycode/server/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- mycode/server/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- mycode/server/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- mycode/server/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- mycode/server/static/assets/matlab-D7o27uSR.js +1 -0
- mycode/server/static/assets/mdc-DTYItulj.js +1 -0
- mycode/server/static/assets/mdx-Cmh6b_Ma.js +1 -0
- mycode/server/static/assets/mermaid-mWjccvbQ.js +1 -0
- mycode/server/static/assets/min-dark-CafNBF8u.js +1 -0
- mycode/server/static/assets/min-light-CTRr51gU.js +1 -0
- mycode/server/static/assets/mipsasm-CKIfxQSi.js +1 -0
- mycode/server/static/assets/mojo-rZm6bMo-.js +1 -0
- mycode/server/static/assets/monokai-D4h5O-jR.js +1 -0
- mycode/server/static/assets/moonbit-_H4v1dQx.js +1 -0
- mycode/server/static/assets/move-IF9eRakj.js +1 -0
- mycode/server/static/assets/narrat-DRg8JJMk.js +1 -0
- mycode/server/static/assets/nextflow-C-mBbutL.js +1 -0
- mycode/server/static/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- mycode/server/static/assets/nginx-BpAMiNFr.js +1 -0
- mycode/server/static/assets/night-owl-C39BiMTA.js +1 -0
- mycode/server/static/assets/night-owl-light-CMTm3GFP.js +1 -0
- mycode/server/static/assets/nim-BIad80T-.js +1 -0
- mycode/server/static/assets/nix-CwoSXNpI.js +1 -0
- mycode/server/static/assets/nord-Ddv68eIx.js +1 -0
- mycode/server/static/assets/nushell-Cz2AlsmD.js +1 -0
- mycode/server/static/assets/objective-c-DXmwc3jG.js +1 -0
- mycode/server/static/assets/objective-cpp-CLxacb5B.js +1 -0
- mycode/server/static/assets/ocaml-C0hk2d4L.js +1 -0
- mycode/server/static/assets/odin-BBf5iR-q.js +1 -0
- mycode/server/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- mycode/server/static/assets/one-light-C3Wv6jpd.js +1 -0
- mycode/server/static/assets/openscad-C4EeE6gA.js +1 -0
- mycode/server/static/assets/pascal-D93ZcfNL.js +1 -0
- mycode/server/static/assets/perl-NvoQZIq0.js +1 -0
- mycode/server/static/assets/php-R6g_5hLQ.js +1 -0
- mycode/server/static/assets/pkl-u5AG7uiY.js +1 -0
- mycode/server/static/assets/plastic-3e1v2bzS.js +1 -0
- mycode/server/static/assets/plsql-ChMvpjG-.js +1 -0
- mycode/server/static/assets/po-BTJTHyun.js +1 -0
- mycode/server/static/assets/poimandres-CS3Unz2-.js +1 -0
- mycode/server/static/assets/polar-C0HS_06l.js +1 -0
- mycode/server/static/assets/postcss-CXtECtnM.js +1 -0
- mycode/server/static/assets/powerquery-CEu0bR-o.js +1 -0
- mycode/server/static/assets/powershell-Dpen1YoG.js +1 -0
- mycode/server/static/assets/prisma-Dd19v3D-.js +1 -0
- mycode/server/static/assets/prolog-CbFg5uaA.js +1 -0
- mycode/server/static/assets/proto-C7zT0LnQ.js +1 -0
- mycode/server/static/assets/pug-DKIMFp6K.js +1 -0
- mycode/server/static/assets/puppet-BMWR74SV.js +1 -0
- mycode/server/static/assets/purescript-CklMAg4u.js +1 -0
- mycode/server/static/assets/python-B6aJPvgy.js +1 -0
- mycode/server/static/assets/qml-3beO22l8.js +1 -0
- mycode/server/static/assets/qmldir-C8lEn-DE.js +1 -0
- mycode/server/static/assets/qss-IeuSbFQv.js +1 -0
- mycode/server/static/assets/r-Dspwwk_N.js +1 -0
- mycode/server/static/assets/racket-BqYA7rlc.js +1 -0
- mycode/server/static/assets/raku-DXvB9xmW.js +1 -0
- mycode/server/static/assets/razor-BDqjjVU7.js +1 -0
- mycode/server/static/assets/red-bN70gL4F.js +1 -0
- mycode/server/static/assets/reg-C-SQnVFl.js +1 -0
- mycode/server/static/assets/regexp-CDVJQ6XC.js +1 -0
- mycode/server/static/assets/rel-C3B-1QV4.js +1 -0
- mycode/server/static/assets/riscv-BM1_JUlF.js +1 -0
- mycode/server/static/assets/ron-D8l8udqQ.js +1 -0
- mycode/server/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- mycode/server/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- mycode/server/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- mycode/server/static/assets/rosmsg-BJDFO7_C.js +1 -0
- mycode/server/static/assets/rst-CRjBmOyv.js +1 -0
- mycode/server/static/assets/ruby-Wjq7vjNf.js +1 -0
- mycode/server/static/assets/rust-B1yitclQ.js +1 -0
- mycode/server/static/assets/sas-cz2c8ADy.js +1 -0
- mycode/server/static/assets/sass-Cj5Yp3dK.js +1 -0
- mycode/server/static/assets/scala-C151Ov-r.js +1 -0
- mycode/server/static/assets/scheme-C98Dy4si.js +1 -0
- mycode/server/static/assets/scss-D5BDwBP9.js +1 -0
- mycode/server/static/assets/sdbl-DVxCFoDh.js +1 -0
- mycode/server/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- mycode/server/static/assets/shellscript-Yzrsuije.js +1 -0
- mycode/server/static/assets/shellsession-BADoaaVG.js +1 -0
- mycode/server/static/assets/slack-dark-BthQWCQV.js +1 -0
- mycode/server/static/assets/slack-ochin-DqwNpetd.js +1 -0
- mycode/server/static/assets/smalltalk-BERRCDM3.js +1 -0
- mycode/server/static/assets/snazzy-light-Bw305WKR.js +1 -0
- mycode/server/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- mycode/server/static/assets/solarized-light-L9t79GZl.js +1 -0
- mycode/server/static/assets/solidity-rGO070M0.js +1 -0
- mycode/server/static/assets/soy-8wufbnw4.js +1 -0
- mycode/server/static/assets/sparql-rVzFXLq3.js +1 -0
- mycode/server/static/assets/splunk-BtCnVYZw.js +1 -0
- mycode/server/static/assets/sql-BLtJtn59.js +1 -0
- mycode/server/static/assets/ssh-config-_ykCGR6B.js +1 -0
- mycode/server/static/assets/stata-BH5u7GGu.js +1 -0
- mycode/server/static/assets/stylus-BEDo0Tqx.js +1 -0
- mycode/server/static/assets/surrealql-Bq5Q-fJD.js +1 -0
- mycode/server/static/assets/svelte-Cy7k_4gC.js +1 -0
- mycode/server/static/assets/swift-D82vCrfD.js +1 -0
- mycode/server/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- mycode/server/static/assets/system-verilog-CnnmHF94.js +1 -0
- mycode/server/static/assets/systemd-4A_iFExJ.js +1 -0
- mycode/server/static/assets/talonscript-CkByrt1z.js +1 -0
- mycode/server/static/assets/tasl-QIJgUcNo.js +1 -0
- mycode/server/static/assets/tcl-dwOrl1Do.js +1 -0
- mycode/server/static/assets/templ-DhtptRzy.js +1 -0
- mycode/server/static/assets/terraform-BETggiCN.js +1 -0
- mycode/server/static/assets/tex-idrVyKtj.js +1 -0
- mycode/server/static/assets/tokyo-night-hegEt444.js +1 -0
- mycode/server/static/assets/toml-vGWfd6FD.js +1 -0
- mycode/server/static/assets/ts-tags-DQrlYJgV.js +1 -0
- mycode/server/static/assets/tsv-B_m7g4N7.js +1 -0
- mycode/server/static/assets/tsx-COt5Ahok.js +1 -0
- mycode/server/static/assets/turtle-BsS91CYL.js +1 -0
- mycode/server/static/assets/twig-xg9kU7Mw.js +1 -0
- mycode/server/static/assets/typescript-BPQ3VLAy.js +1 -0
- mycode/server/static/assets/typespec-CAFt9gP4.js +1 -0
- mycode/server/static/assets/typst-DHCkPAjA.js +1 -0
- mycode/server/static/assets/v-BcVCzyr7.js +1 -0
- mycode/server/static/assets/vala-CsfeWuGM.js +1 -0
- mycode/server/static/assets/vb-D17OF-Vu.js +1 -0
- mycode/server/static/assets/verilog-BQ8w6xss.js +1 -0
- mycode/server/static/assets/vesper-DU1UobuO.js +1 -0
- mycode/server/static/assets/vhdl-CeAyd5Ju.js +1 -0
- mycode/server/static/assets/viml-CJc9bBzg.js +1 -0
- mycode/server/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- mycode/server/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- mycode/server/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- mycode/server/static/assets/vue-D2xRrEX4.js +1 -0
- mycode/server/static/assets/vue-html-AaS7Mt5G.js +1 -0
- mycode/server/static/assets/vue-vine-BoDAl6tE.js +1 -0
- mycode/server/static/assets/vyper-CDx5xZoG.js +1 -0
- mycode/server/static/assets/wasm-CG6Dc4jp.js +1 -0
- mycode/server/static/assets/wasm-MzD3tlZU.js +1 -0
- mycode/server/static/assets/wenyan-BV7otONQ.js +1 -0
- mycode/server/static/assets/wgsl-Dx-B1_4e.js +1 -0
- mycode/server/static/assets/wikitext-BhOHFoWU.js +1 -0
- mycode/server/static/assets/wit-5i3qLPDT.js +1 -0
- mycode/server/static/assets/wolfram-lXgVvXCa.js +1 -0
- mycode/server/static/assets/xml-sdJ4AIDG.js +1 -0
- mycode/server/static/assets/xsl-CtQFsRM5.js +1 -0
- mycode/server/static/assets/yaml-Buea-lGh.js +1 -0
- mycode/server/static/assets/zenscript-DVFEvuxE.js +1 -0
- mycode/server/static/assets/zig-VOosw3JB.js +1 -0
- mycode/server/static/favicon_slashes.svg +12 -0
- mycode/server/static/index.html +35 -0
- mycode_cli-0.1.0.dist-info/METADATA +186 -0
- mycode_cli-0.1.0.dist-info/RECORD +404 -0
- mycode_cli-0.1.0.dist-info/WHEEL +4 -0
- mycode_cli-0.1.0.dist-info/entry_points.txt +2 -0
- 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
|
+
)
|