pythinker-code 2.0.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.
- pythinker_code/CHANGELOG.md +16 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +92 -0
- pythinker_code/acp/AGENTS.md +93 -0
- pythinker_code/acp/__init__.py +13 -0
- pythinker_code/acp/convert.py +128 -0
- pythinker_code/acp/host.py +298 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +496 -0
- pythinker_code/acp/tools.py +167 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +36 -0
- pythinker_code/agents/default/coder.yaml +25 -0
- pythinker_code/agents/default/explore.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +30 -0
- pythinker_code/agents/default/system.md +164 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +820 -0
- pythinker_code/approval_runtime/__init__.py +29 -0
- pythinker_code/approval_runtime/models.py +42 -0
- pythinker_code/approval_runtime/runtime.py +235 -0
- pythinker_code/auth/__init__.py +25 -0
- pythinker_code/auth/anthropic_direct.py +207 -0
- pythinker_code/auth/deepseek.py +192 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1122 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +771 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +466 -0
- pythinker_code/background/__init__.py +36 -0
- pythinker_code/background/agent_runner.py +231 -0
- pythinker_code/background/ids.py +19 -0
- pythinker_code/background/manager.py +650 -0
- pythinker_code/background/models.py +105 -0
- pythinker_code/background/store.py +237 -0
- pythinker_code/background/summary.py +66 -0
- pythinker_code/background/worker.py +209 -0
- pythinker_code/cli/__init__.py +1326 -0
- pythinker_code/cli/__main__.py +19 -0
- pythinker_code/cli/_lazy_group.py +238 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +349 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +453 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +371 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +89 -0
- pythinker_code/llm.py +412 -0
- pythinker_code/metadata.py +79 -0
- pythinker_code/notifications/__init__.py +33 -0
- pythinker_code/notifications/llm.py +77 -0
- pythinker_code/notifications/manager.py +145 -0
- pythinker_code/notifications/models.py +50 -0
- pythinker_code/notifications/notifier.py +41 -0
- pythinker_code/notifications/store.py +118 -0
- pythinker_code/notifications/wire.py +21 -0
- pythinker_code/plugin/__init__.py +124 -0
- pythinker_code/plugin/manager.py +153 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompts/__init__.py +6 -0
- pythinker_code/prompts/compact.md +73 -0
- pythinker_code/prompts/init.md +21 -0
- pythinker_code/py.typed +0 -0
- pythinker_code/session.py +319 -0
- pythinker_code/session_fork.py +325 -0
- pythinker_code/session_state.py +132 -0
- pythinker_code/share.py +14 -0
- pythinker_code/skill/__init__.py +727 -0
- pythinker_code/skill/flow/__init__.py +99 -0
- pythinker_code/skill/flow/d2.py +482 -0
- pythinker_code/skill/flow/mermaid.py +266 -0
- pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
- pythinker_code/skills/skill-creator/SKILL.md +367 -0
- pythinker_code/soul/__init__.py +304 -0
- pythinker_code/soul/agent.py +520 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +214 -0
- pythinker_code/soul/compaction.py +189 -0
- pythinker_code/soul/context.py +339 -0
- pythinker_code/soul/denwarenji.py +39 -0
- pythinker_code/soul/dynamic_injection.py +84 -0
- pythinker_code/soul/dynamic_injections/__init__.py +0 -0
- pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
- pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
- pythinker_code/soul/message.py +92 -0
- pythinker_code/soul/pythinkersoul.py +1613 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +788 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +42 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +54 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +428 -0
- pythinker_code/subagents/store.py +196 -0
- pythinker_code/telemetry/__init__.py +211 -0
- pythinker_code/telemetry/config.py +54 -0
- pythinker_code/telemetry/crash.py +157 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +240 -0
- pythinker_code/telemetry/sentry.py +167 -0
- pythinker_code/telemetry/sink.py +189 -0
- pythinker_code/tools/AGENTS.md +6 -0
- pythinker_code/tools/__init__.py +105 -0
- pythinker_code/tools/agent/__init__.py +277 -0
- pythinker_code/tools/agent/description.md +41 -0
- pythinker_code/tools/ask_user/__init__.py +159 -0
- pythinker_code/tools/ask_user/description.md +19 -0
- pythinker_code/tools/background/__init__.py +318 -0
- pythinker_code/tools/background/list.md +10 -0
- pythinker_code/tools/background/output.md +11 -0
- pythinker_code/tools/background/stop.md +8 -0
- pythinker_code/tools/display.py +46 -0
- pythinker_code/tools/dmail/__init__.py +38 -0
- pythinker_code/tools/dmail/dmail.md +17 -0
- pythinker_code/tools/file/__init__.py +30 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +160 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +589 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +300 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +217 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +195 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +177 -0
- pythinker_code/tools/plan/__init__.py +327 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +190 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +253 -0
- pythinker_code/tools/shell/bash.md +35 -0
- pythinker_code/tools/shell/powershell.md +30 -0
- pythinker_code/tools/test.py +55 -0
- pythinker_code/tools/think/__init__.py +21 -0
- pythinker_code/tools/think/think.md +1 -0
- pythinker_code/tools/todo/__init__.py +168 -0
- pythinker_code/tools/todo/set_todo_list.md +23 -0
- pythinker_code/tools/utils.py +199 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +189 -0
- pythinker_code/tools/web/search.md +1 -0
- pythinker_code/tools/web/search.py +163 -0
- pythinker_code/ui/__init__.py +0 -0
- pythinker_code/ui/acp/__init__.py +99 -0
- pythinker_code/ui/print/__init__.py +474 -0
- pythinker_code/ui/print/visualize.py +185 -0
- pythinker_code/ui/shell/__init__.py +1696 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +17 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/mcp_status.py +113 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +272 -0
- pythinker_code/ui/shell/placeholders.py +531 -0
- pythinker_code/ui/shell/prompt.py +2278 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +898 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/update.py +350 -0
- pythinker_code/ui/shell/usage.py +291 -0
- pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
- pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
- pythinker_code/ui/shell/usage_adapters/base.py +72 -0
- pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
- pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
- pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
- pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
- pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
- pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
- pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
- pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
- pythinker_code/ui/shell/usage_render.py +150 -0
- pythinker_code/ui/shell/visualize/__init__.py +165 -0
- pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
- pythinker_code/ui/shell/visualize/_blocks.py +629 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +523 -0
- pythinker_code/ui/shell/visualize/_live_view.py +826 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/theme.py +241 -0
- pythinker_code/usage_ratelimit_cache.py +175 -0
- pythinker_code/utils/__init__.py +0 -0
- pythinker_code/utils/aiohttp.py +24 -0
- pythinker_code/utils/aioqueue.py +72 -0
- pythinker_code/utils/broadcast.py +37 -0
- pythinker_code/utils/changelog.py +108 -0
- pythinker_code/utils/clipboard.py +246 -0
- pythinker_code/utils/datetime.py +64 -0
- pythinker_code/utils/diff.py +135 -0
- pythinker_code/utils/editor.py +91 -0
- pythinker_code/utils/environment.py +73 -0
- pythinker_code/utils/envvar.py +22 -0
- pythinker_code/utils/export.py +696 -0
- pythinker_code/utils/file_filter.py +375 -0
- pythinker_code/utils/frontmatter.py +70 -0
- pythinker_code/utils/io.py +27 -0
- pythinker_code/utils/logging.py +146 -0
- pythinker_code/utils/media_tags.py +29 -0
- pythinker_code/utils/message.py +24 -0
- pythinker_code/utils/path.py +199 -0
- pythinker_code/utils/proctitle.py +33 -0
- pythinker_code/utils/proxy.py +31 -0
- pythinker_code/utils/pyinstaller.py +45 -0
- pythinker_code/utils/rich/__init__.py +33 -0
- pythinker_code/utils/rich/columns.py +99 -0
- pythinker_code/utils/rich/diff_render.py +481 -0
- pythinker_code/utils/rich/markdown.py +900 -0
- pythinker_code/utils/rich/markdown_sample.md +108 -0
- pythinker_code/utils/rich/markdown_sample_short.md +2 -0
- pythinker_code/utils/rich/syntax.py +114 -0
- pythinker_code/utils/sensitive.py +54 -0
- pythinker_code/utils/server.py +121 -0
- pythinker_code/utils/signals.py +43 -0
- pythinker_code/utils/slashcmd.py +124 -0
- pythinker_code/utils/string.py +41 -0
- pythinker_code/utils/subprocess_env.py +73 -0
- pythinker_code/utils/term.py +168 -0
- pythinker_code/utils/typing.py +20 -0
- pythinker_code/vis/__init__.py +0 -0
- pythinker_code/vis/api/__init__.py +5 -0
- pythinker_code/vis/api/sessions.py +687 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +175 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
- pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/vis/static/index.html +17 -0
- pythinker_code/web/__init__.py +5 -0
- pythinker_code/web/api/__init__.py +15 -0
- pythinker_code/web/api/config.py +208 -0
- pythinker_code/web/api/open_in.py +199 -0
- pythinker_code/web/api/sessions.py +1225 -0
- pythinker_code/web/app.py +451 -0
- pythinker_code/web/auth.py +191 -0
- pythinker_code/web/models.py +98 -0
- pythinker_code/web/runner/__init__.py +5 -0
- pythinker_code/web/runner/messages.py +57 -0
- pythinker_code/web/runner/process.py +754 -0
- pythinker_code/web/runner/worker.py +97 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
- pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
- pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
- pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
- pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
- pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
- pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
- pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
- pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
- pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
- pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
- pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
- pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
- pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
- pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
- pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
- pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
- pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
- pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
- pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
- pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
- pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
- pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
- pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
- pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
- pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
- pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
- pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
- pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
- pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
- pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
- pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
- pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
- pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
- pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
- pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
- pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
- pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
- pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
- pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
- pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
- pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
- pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
- pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
- pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
- pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
- pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
- pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
- pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
- pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
- pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
- pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
- pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
- pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
- pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
- pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
- pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
- pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
- pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
- pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
- pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
- pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
- pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
- pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
- pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
- pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
- pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
- pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
- pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
- pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
- pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
- pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
- pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
- pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
- pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
- pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
- pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
- pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
- pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
- pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
- pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
- pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
- pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
- pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
- pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
- pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
- pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
- pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
- pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
- pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
- pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
- pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
- pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
- pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
- pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
- pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
- pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
- pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
- pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
- pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
- pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
- pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
- pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
- pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
- pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
- pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
- pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
- pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
- pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
- pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
- pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
- pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
- pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
- pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
- pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
- pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
- pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
- pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
- pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
- pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
- pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
- pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
- pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
- pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
- pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
- pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
- pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
- pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
- pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
- pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
- pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
- pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
- pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
- pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
- pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
- pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
- pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
- pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
- pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
- pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
- pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
- pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
- pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
- pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
- pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
- pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
- pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
- pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
- pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
- pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
- pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
- pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
- pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
- pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
- pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
- pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
- pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
- pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
- pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
- pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
- pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
- pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
- pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
- pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
- pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
- pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
- pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
- pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
- pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
- pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
- pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
- pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
- pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
- pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
- pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
- pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
- pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
- pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
- pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
- pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
- pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
- pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
- pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
- pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
- pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
- pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
- pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
- pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
- pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
- pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
- pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
- pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
- pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
- pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
- pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
- pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
- pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
- pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
- pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
- pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
- pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
- pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
- pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
- pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
- pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
- pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
- pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
- pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
- pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
- pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
- pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
- pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
- pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
- pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
- pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
- pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
- pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
- pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
- pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
- pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
- pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
- pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
- pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
- pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
- pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
- pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
- pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
- pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
- pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
- pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
- pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
- pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
- pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
- pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
- pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
- pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
- pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
- pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
- pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
- pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
- pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
- pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
- pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
- pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
- pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
- pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
- pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
- pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
- pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
- pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
- pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
- pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
- pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
- pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
- pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
- pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
- pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
- pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
- pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
- pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
- pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
- pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
- pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
- pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
- pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
- pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
- pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
- pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
- pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
- pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
- pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
- pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
- pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
- pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
- pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
- pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
- pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
- pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
- pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
- pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
- pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
- pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
- pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
- pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
- pythinker_code/web/static/brand/arctecture.webp +0 -0
- pythinker_code/web/static/brand/bimi-logo.svg +46 -0
- pythinker_code/web/static/brand/favicon.ico +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
- pythinker_code/web/static/brand/icon-192.png +0 -0
- pythinker_code/web/static/brand/icon-512.png +0 -0
- pythinker_code/web/static/brand/icon.svg +1 -0
- pythinker_code/web/static/brand/logo.png +0 -0
- pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
- pythinker_code/web/static/brand/robots.txt +4 -0
- pythinker_code/web/static/index.html +15 -0
- pythinker_code/web/static/logo.png +0 -0
- pythinker_code/web/store/__init__.py +1 -0
- pythinker_code/web/store/sessions.py +432 -0
- pythinker_code/wire/__init__.py +148 -0
- pythinker_code/wire/file.py +151 -0
- pythinker_code/wire/jsonrpc.py +263 -0
- pythinker_code/wire/protocol.py +2 -0
- pythinker_code/wire/root_hub.py +27 -0
- pythinker_code/wire/serde.py +26 -0
- pythinker_code/wire/server.py +1069 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-2.0.0.dist-info/METADATA +660 -0
- pythinker_code-2.0.0.dist-info/RECORD +731 -0
- pythinker_code-2.0.0.dist-info/WHEEL +4 -0
- pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
- pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-2.0.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Literal, cast, override
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from pythinker_core.tooling import CallableTool2, ToolReturnValue
|
|
7
|
+
|
|
8
|
+
from pythinker_code.session_state import TodoItemState
|
|
9
|
+
from pythinker_code.soul.agent import Runtime
|
|
10
|
+
from pythinker_code.tools.display import TodoDisplayBlock, TodoDisplayItem
|
|
11
|
+
from pythinker_code.tools.utils import load_desc
|
|
12
|
+
from pythinker_code.utils.logging import logger
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Todo(BaseModel):
|
|
16
|
+
title: str = Field(description="The title of the todo", min_length=1)
|
|
17
|
+
status: Literal["pending", "in_progress", "done"] = Field(description="The status of the todo")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Params(BaseModel):
|
|
21
|
+
todos: list[Todo] | None = Field(
|
|
22
|
+
default=None,
|
|
23
|
+
description=(
|
|
24
|
+
"The updated todo list. "
|
|
25
|
+
"If not provided, returns the current todo list without making changes."
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SetTodoList(CallableTool2[Params]):
|
|
31
|
+
name: str = "SetTodoList"
|
|
32
|
+
description: str = load_desc(Path(__file__).parent / "set_todo_list.md")
|
|
33
|
+
params: type[Params] = Params
|
|
34
|
+
|
|
35
|
+
def __init__(self, runtime: Runtime) -> None:
|
|
36
|
+
super().__init__()
|
|
37
|
+
self._runtime = runtime
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
async def __call__(self, params: Params) -> ToolReturnValue:
|
|
41
|
+
if params.todos is None:
|
|
42
|
+
return self._read_todos()
|
|
43
|
+
return self._write_todos(params.todos)
|
|
44
|
+
|
|
45
|
+
# ---- Write mode --------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
def _write_todos(self, todos: list[Todo]) -> ToolReturnValue:
|
|
48
|
+
"""Persist the todo list and return confirmation."""
|
|
49
|
+
self._save_todos(todos)
|
|
50
|
+
|
|
51
|
+
items = [TodoDisplayItem(title=todo.title, status=todo.status) for todo in todos]
|
|
52
|
+
return ToolReturnValue(
|
|
53
|
+
is_error=False,
|
|
54
|
+
output="Todo list updated",
|
|
55
|
+
message="Todo list updated",
|
|
56
|
+
display=[TodoDisplayBlock(items=items)],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# ---- Read mode ---------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
def _read_todos(self) -> ToolReturnValue:
|
|
62
|
+
"""Return the current todo list as text output for the model."""
|
|
63
|
+
todos = self._load_todos()
|
|
64
|
+
if not todos:
|
|
65
|
+
return ToolReturnValue(
|
|
66
|
+
is_error=False,
|
|
67
|
+
output="Todo list is empty.",
|
|
68
|
+
message="",
|
|
69
|
+
display=[],
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
lines: list[str] = ["Current todo list:"]
|
|
73
|
+
for todo in todos:
|
|
74
|
+
lines.append(f"- [{todo.status}] {todo.title}")
|
|
75
|
+
return ToolReturnValue(
|
|
76
|
+
is_error=False,
|
|
77
|
+
output="\n".join(lines),
|
|
78
|
+
message="",
|
|
79
|
+
display=[],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# ---- Persistence -------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
def _save_todos(self, todos: list[Todo]) -> None:
|
|
85
|
+
"""Persist todos to the appropriate state file."""
|
|
86
|
+
items = [TodoItemState(title=t.title, status=t.status) for t in todos]
|
|
87
|
+
|
|
88
|
+
if self._runtime.role == "root":
|
|
89
|
+
self._save_root_todos(items)
|
|
90
|
+
else:
|
|
91
|
+
self._save_subagent_todos(items)
|
|
92
|
+
|
|
93
|
+
def _load_todos(self) -> list[Todo]:
|
|
94
|
+
"""Load todos from the appropriate state file."""
|
|
95
|
+
if self._runtime.role == "root":
|
|
96
|
+
return self._load_root_todos()
|
|
97
|
+
else:
|
|
98
|
+
return self._load_subagent_todos()
|
|
99
|
+
|
|
100
|
+
def _save_root_todos(self, items: list[TodoItemState]) -> None:
|
|
101
|
+
session = self._runtime.session
|
|
102
|
+
session.state.todos = items
|
|
103
|
+
session.save_state()
|
|
104
|
+
|
|
105
|
+
def _load_root_todos(self) -> list[Todo]:
|
|
106
|
+
from pythinker_code.session_state import load_session_state
|
|
107
|
+
|
|
108
|
+
session = self._runtime.session
|
|
109
|
+
fresh = load_session_state(session.dir)
|
|
110
|
+
session.state.todos = fresh.todos
|
|
111
|
+
result: list[Todo] = []
|
|
112
|
+
for t in fresh.todos:
|
|
113
|
+
try:
|
|
114
|
+
result.append(Todo(title=t.title, status=t.status))
|
|
115
|
+
except Exception:
|
|
116
|
+
logger.warning("Skipping malformed todo item in root state: {t}", t=t)
|
|
117
|
+
return result
|
|
118
|
+
|
|
119
|
+
def _save_subagent_todos(self, items: list[TodoItemState]) -> None:
|
|
120
|
+
state_file = self._subagent_state_file()
|
|
121
|
+
if state_file is None:
|
|
122
|
+
return
|
|
123
|
+
data = self._read_subagent_state(state_file)
|
|
124
|
+
data["todos"] = [item.model_dump() for item in items]
|
|
125
|
+
self._write_subagent_state(state_file, data)
|
|
126
|
+
|
|
127
|
+
def _load_subagent_todos(self) -> list[Todo]:
|
|
128
|
+
state_file = self._subagent_state_file()
|
|
129
|
+
if state_file is None:
|
|
130
|
+
return []
|
|
131
|
+
data = self._read_subagent_state(state_file)
|
|
132
|
+
raw_todos_val = data.get("todos", [])
|
|
133
|
+
raw_todos = cast(list[Any], raw_todos_val) if isinstance(raw_todos_val, list) else []
|
|
134
|
+
result: list[Todo] = []
|
|
135
|
+
for item in raw_todos:
|
|
136
|
+
try:
|
|
137
|
+
result.append(Todo(**item))
|
|
138
|
+
except Exception:
|
|
139
|
+
logger.warning("Skipping malformed todo item in subagent state: {item}", item=item)
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
def _subagent_state_file(self) -> Path | None:
|
|
143
|
+
store = self._runtime.subagent_store
|
|
144
|
+
agent_id = self._runtime.subagent_id
|
|
145
|
+
if store is None or agent_id is None:
|
|
146
|
+
return None
|
|
147
|
+
return store.instance_dir(agent_id) / "state.json"
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _read_subagent_state(path: Path) -> dict[str, Any]:
|
|
151
|
+
if not path.exists():
|
|
152
|
+
return {}
|
|
153
|
+
try:
|
|
154
|
+
data = json.loads(path.read_text(encoding="utf-8", errors="replace"))
|
|
155
|
+
except (json.JSONDecodeError, OSError, UnicodeDecodeError):
|
|
156
|
+
logger.warning("Corrupted subagent todo state, using defaults: {path}", path=path)
|
|
157
|
+
return {}
|
|
158
|
+
if not isinstance(data, dict):
|
|
159
|
+
logger.warning("Invalid subagent todo state type, using defaults: {path}", path=path)
|
|
160
|
+
return {}
|
|
161
|
+
return cast(dict[str, Any], data)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _write_subagent_state(path: Path, data: dict[str, Any]) -> None:
|
|
165
|
+
from pythinker_code.utils.io import atomic_json_write
|
|
166
|
+
|
|
167
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
atomic_json_write(data, path)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Manage your todo list for tracking task progress.
|
|
2
|
+
|
|
3
|
+
Todo list is a simple yet powerful tool to help you get things done. You typically want to use this tool when the given task involves multiple subtasks/milestones, or, multiple tasks are given in a single request. This tool can help you to break down the task and track the progress.
|
|
4
|
+
|
|
5
|
+
**Usage modes:**
|
|
6
|
+
|
|
7
|
+
- **Update mode**: Pass `todos` to set the entire todo list. The previous list is replaced.
|
|
8
|
+
- **Query mode**: Omit `todos` (or pass null) to retrieve the current todo list without changes.
|
|
9
|
+
- **Clear mode**: Pass an empty array `[]` to clear all todos.
|
|
10
|
+
|
|
11
|
+
This is the only todo list tool available to you. That said, each time you want to update the todo list, you need to provide the whole list. Make sure to maintain the todo items and their statuses properly.
|
|
12
|
+
|
|
13
|
+
Once you finished a subtask/milestone, remember to update the todo list to reflect the progress. Also, you can give yourself a self-encouragement to keep you motivated.
|
|
14
|
+
|
|
15
|
+
Abusing this tool to track too small steps will just waste your time and make your context messy. For example, here are some cases you should not use this tool:
|
|
16
|
+
|
|
17
|
+
- When the user just simply ask you a question. E.g. "What language and framework is used in the project?", "What is the best practice for x?"
|
|
18
|
+
- When it only takes a few steps/tool calls to complete the task. E.g. "Fix the unit test function 'test_xxx'", "Refactor the function 'xxx' to make it more solid."
|
|
19
|
+
- When the user prompt is very specific and the only thing you need to do is brainlessly following the instructions. E.g. "Replace xxx to yyy in the file zzz", "Create a file xxx with content yyy."
|
|
20
|
+
|
|
21
|
+
However, do not get stuck in a rut. Be flexible. Sometimes, you may try to use todo list at first, then realize the task is too simple and you can simply stop using it; or, sometimes, you may realize the task is complex after a few steps and then you can start using todo list to break it down.
|
|
22
|
+
|
|
23
|
+
IMPORTANT: Do not call this tool repeatedly without making real progress on at least one task between calls. If you are unsure about the current state, use Query mode (omit `todos`) to check before updating. If you find yourself unable to advance any task with your available tools, inform the user about what is blocking you instead of replanning. Repeatedly updating the todo list without doing actual work is counterproductive.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from jinja2 import Environment, Undefined
|
|
5
|
+
from pythinker_core.tooling import BriefDisplayBlock, DisplayBlock, ToolError, ToolReturnValue
|
|
6
|
+
from pythinker_core.utils.typing import JsonType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _KeepPlaceholderUndefined(Undefined):
|
|
10
|
+
def __str__(self) -> str:
|
|
11
|
+
if self._undefined_name is None:
|
|
12
|
+
return ""
|
|
13
|
+
return f"${{{self._undefined_name}}}"
|
|
14
|
+
|
|
15
|
+
__repr__ = __str__
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_desc(path: Path, context: dict[str, object] | None = None) -> str:
|
|
19
|
+
"""Load a tool description from a file, rendered via Jinja2."""
|
|
20
|
+
description = path.read_text(encoding="utf-8")
|
|
21
|
+
env = Environment(
|
|
22
|
+
keep_trailing_newline=True,
|
|
23
|
+
lstrip_blocks=True,
|
|
24
|
+
trim_blocks=True,
|
|
25
|
+
variable_start_string="${",
|
|
26
|
+
variable_end_string="}",
|
|
27
|
+
undefined=_KeepPlaceholderUndefined,
|
|
28
|
+
)
|
|
29
|
+
template = env.from_string(description)
|
|
30
|
+
return template.render(context or {})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def truncate_line(line: str, max_length: int, marker: str = "...") -> str:
|
|
34
|
+
"""
|
|
35
|
+
Truncate a line if it exceeds `max_length`, preserving the beginning and the line break.
|
|
36
|
+
The output may be longer than `max_length` if it is too short to fit the marker.
|
|
37
|
+
"""
|
|
38
|
+
if len(line) <= max_length:
|
|
39
|
+
return line
|
|
40
|
+
|
|
41
|
+
# Find line breaks at the end of the line
|
|
42
|
+
m = re.search(r"[\r\n]+$", line)
|
|
43
|
+
linebreak = m.group(0) if m else ""
|
|
44
|
+
end = marker + linebreak
|
|
45
|
+
max_length = max(max_length, len(end))
|
|
46
|
+
return line[: max_length - len(end)] + end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Default output limits
|
|
50
|
+
DEFAULT_MAX_CHARS = 50_000
|
|
51
|
+
DEFAULT_MAX_LINE_LENGTH = 2000
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ToolResultBuilder:
|
|
55
|
+
"""
|
|
56
|
+
Builder for tool results with character and line limits.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
max_chars: int = DEFAULT_MAX_CHARS,
|
|
62
|
+
max_line_length: int | None = DEFAULT_MAX_LINE_LENGTH,
|
|
63
|
+
):
|
|
64
|
+
self.max_chars = max_chars
|
|
65
|
+
self.max_line_length = max_line_length
|
|
66
|
+
self._marker = "[...truncated]"
|
|
67
|
+
if max_line_length is not None:
|
|
68
|
+
assert max_line_length > len(self._marker)
|
|
69
|
+
self._buffer: list[str] = []
|
|
70
|
+
self._n_chars = 0
|
|
71
|
+
self._n_lines = 0
|
|
72
|
+
self._truncation_happened = False
|
|
73
|
+
self._display: list[DisplayBlock] = []
|
|
74
|
+
self._extras: dict[str, JsonType] | None = None
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def is_full(self) -> bool:
|
|
78
|
+
"""Check if output buffer is full due to character limit."""
|
|
79
|
+
return self._n_chars >= self.max_chars
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def n_chars(self) -> int:
|
|
83
|
+
"""Get current character count."""
|
|
84
|
+
return self._n_chars
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def n_lines(self) -> int:
|
|
88
|
+
"""Get current line count."""
|
|
89
|
+
return self._n_lines
|
|
90
|
+
|
|
91
|
+
def write(self, text: str) -> int:
|
|
92
|
+
"""
|
|
93
|
+
Write text to the output buffer.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
int: Number of characters actually written
|
|
97
|
+
"""
|
|
98
|
+
if self.is_full:
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
lines = text.splitlines(keepends=True)
|
|
102
|
+
if not lines:
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
chars_written = 0
|
|
106
|
+
|
|
107
|
+
for line in lines:
|
|
108
|
+
if self.is_full:
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
original_line = line
|
|
112
|
+
remaining_chars = self.max_chars - self._n_chars
|
|
113
|
+
limit = (
|
|
114
|
+
min(remaining_chars, self.max_line_length)
|
|
115
|
+
if self.max_line_length is not None
|
|
116
|
+
else remaining_chars
|
|
117
|
+
)
|
|
118
|
+
line = truncate_line(line, limit, self._marker)
|
|
119
|
+
if line != original_line:
|
|
120
|
+
self._truncation_happened = True
|
|
121
|
+
|
|
122
|
+
self._buffer.append(line)
|
|
123
|
+
chars_written += len(line)
|
|
124
|
+
self._n_chars += len(line)
|
|
125
|
+
if line.endswith("\n"):
|
|
126
|
+
self._n_lines += 1
|
|
127
|
+
|
|
128
|
+
return chars_written
|
|
129
|
+
|
|
130
|
+
def display(self, *blocks: DisplayBlock) -> None:
|
|
131
|
+
"""Add display blocks to the tool result."""
|
|
132
|
+
self._display.extend(blocks)
|
|
133
|
+
|
|
134
|
+
def extras(self, **extras: JsonType) -> None:
|
|
135
|
+
"""Add extra data to the tool result."""
|
|
136
|
+
if self._extras is None:
|
|
137
|
+
self._extras = {}
|
|
138
|
+
self._extras.update(extras)
|
|
139
|
+
|
|
140
|
+
def ok(self, message: str = "", *, brief: str = "") -> ToolReturnValue:
|
|
141
|
+
"""Create a ToolReturnValue with is_error=False and the current output."""
|
|
142
|
+
output = "".join(self._buffer)
|
|
143
|
+
|
|
144
|
+
final_message = message
|
|
145
|
+
if final_message and not final_message.endswith("."):
|
|
146
|
+
final_message += "."
|
|
147
|
+
truncation_msg = "Output is truncated to fit in the message."
|
|
148
|
+
if self._truncation_happened:
|
|
149
|
+
if final_message:
|
|
150
|
+
final_message += f" {truncation_msg}"
|
|
151
|
+
else:
|
|
152
|
+
final_message = truncation_msg
|
|
153
|
+
return ToolReturnValue(
|
|
154
|
+
is_error=False,
|
|
155
|
+
output=output,
|
|
156
|
+
message=final_message,
|
|
157
|
+
display=([BriefDisplayBlock(text=brief)] if brief else []) + self._display,
|
|
158
|
+
extras=self._extras,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def error(self, message: str, *, brief: str) -> ToolReturnValue:
|
|
162
|
+
"""Create a ToolReturnValue with is_error=True and the current output."""
|
|
163
|
+
output = "".join(self._buffer)
|
|
164
|
+
|
|
165
|
+
final_message = message
|
|
166
|
+
if self._truncation_happened:
|
|
167
|
+
truncation_msg = "Output is truncated to fit in the message."
|
|
168
|
+
if final_message:
|
|
169
|
+
final_message += f" {truncation_msg}"
|
|
170
|
+
else:
|
|
171
|
+
final_message = truncation_msg
|
|
172
|
+
|
|
173
|
+
return ToolReturnValue(
|
|
174
|
+
is_error=True,
|
|
175
|
+
output=output,
|
|
176
|
+
message=final_message,
|
|
177
|
+
display=([BriefDisplayBlock(text=brief)] if brief else []) + self._display,
|
|
178
|
+
extras=self._extras,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class ToolRejectedError(ToolError):
|
|
183
|
+
has_feedback: bool = False
|
|
184
|
+
|
|
185
|
+
def __init__(
|
|
186
|
+
self,
|
|
187
|
+
message: str | None = None,
|
|
188
|
+
brief: str = "Rejected by user",
|
|
189
|
+
has_feedback: bool = False,
|
|
190
|
+
):
|
|
191
|
+
super().__init__(
|
|
192
|
+
message=message
|
|
193
|
+
or (
|
|
194
|
+
"The tool call is rejected by the user. "
|
|
195
|
+
"Stop what you are doing and wait for the user to tell you how to proceed."
|
|
196
|
+
),
|
|
197
|
+
brief=brief,
|
|
198
|
+
)
|
|
199
|
+
self.has_feedback = has_feedback
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Fetch a web page from a URL and extract main text content from it.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
import aiohttp
|
|
5
|
+
import trafilatura
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from pythinker_core.tooling import CallableTool2, ToolReturnValue
|
|
8
|
+
|
|
9
|
+
from pythinker_code.config import Config
|
|
10
|
+
from pythinker_code.constant import USER_AGENT
|
|
11
|
+
from pythinker_code.soul.agent import Runtime
|
|
12
|
+
from pythinker_code.soul.toolset import get_current_tool_call_or_none
|
|
13
|
+
from pythinker_code.tools.utils import ToolResultBuilder, load_desc
|
|
14
|
+
from pythinker_code.utils.aiohttp import new_client_session
|
|
15
|
+
from pythinker_code.utils.logging import logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Params(BaseModel):
|
|
19
|
+
url: str = Field(description="The URL to fetch content from.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FetchURL(CallableTool2[Params]):
|
|
23
|
+
name: str = "FetchURL"
|
|
24
|
+
description: str = load_desc(Path(__file__).parent / "fetch.md", {})
|
|
25
|
+
params: type[Params] = Params
|
|
26
|
+
|
|
27
|
+
def __init__(self, config: Config, runtime: Runtime):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self._runtime = runtime
|
|
30
|
+
self._service_config = config.services.pythinker_ai_fetch
|
|
31
|
+
|
|
32
|
+
@override
|
|
33
|
+
async def __call__(self, params: Params) -> ToolReturnValue:
|
|
34
|
+
if self._service_config:
|
|
35
|
+
ret = await self._fetch_with_service(params)
|
|
36
|
+
if not ret.is_error:
|
|
37
|
+
return ret
|
|
38
|
+
logger.warning("Failed to fetch URL via service: {error}", error=ret.message)
|
|
39
|
+
# fallback to local fetch if service fetch fails
|
|
40
|
+
return await self.fetch_with_http_get(params)
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
async def fetch_with_http_get(params: Params) -> ToolReturnValue:
|
|
44
|
+
builder = ToolResultBuilder(max_line_length=None)
|
|
45
|
+
try:
|
|
46
|
+
# Fetching arbitrary web pages can take a while on large/slow sites.
|
|
47
|
+
fetch_timeout = aiohttp.ClientTimeout(total=180, sock_read=60, sock_connect=15)
|
|
48
|
+
async with (
|
|
49
|
+
new_client_session(timeout=fetch_timeout) as session,
|
|
50
|
+
session.get(
|
|
51
|
+
params.url,
|
|
52
|
+
headers={
|
|
53
|
+
"User-Agent": (
|
|
54
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
55
|
+
"(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
) as response,
|
|
59
|
+
):
|
|
60
|
+
if response.status >= 400:
|
|
61
|
+
logger.warning(
|
|
62
|
+
"FetchURL HTTP error: status={status}, url={url}",
|
|
63
|
+
status=response.status,
|
|
64
|
+
url=params.url,
|
|
65
|
+
)
|
|
66
|
+
return builder.error(
|
|
67
|
+
(
|
|
68
|
+
f"Failed to fetch URL. Status: {response.status}. "
|
|
69
|
+
f"This may indicate the page is not accessible or the server is down."
|
|
70
|
+
),
|
|
71
|
+
brief=f"HTTP {response.status} error",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
resp_text = (await response.read()).decode("utf-8", errors="replace")
|
|
75
|
+
|
|
76
|
+
content_type = response.headers.get(aiohttp.hdrs.CONTENT_TYPE, "").lower()
|
|
77
|
+
if content_type.startswith(("text/plain", "text/markdown")):
|
|
78
|
+
builder.write(resp_text)
|
|
79
|
+
return builder.ok("The returned content is the full content of the page.")
|
|
80
|
+
except TimeoutError:
|
|
81
|
+
logger.warning("FetchURL timed out: url={url}", url=params.url)
|
|
82
|
+
return builder.error(
|
|
83
|
+
"Failed to fetch URL: request timed out. The server may be slow or unreachable.",
|
|
84
|
+
brief="Request timed out",
|
|
85
|
+
)
|
|
86
|
+
except aiohttp.ClientError as e:
|
|
87
|
+
logger.warning("FetchURL network error: {error}, url={url}", error=e, url=params.url)
|
|
88
|
+
return builder.error(
|
|
89
|
+
(
|
|
90
|
+
f"Failed to fetch URL due to network error: {e}. "
|
|
91
|
+
"This may indicate the URL is invalid or the server is unreachable."
|
|
92
|
+
),
|
|
93
|
+
brief="Network error",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if not resp_text:
|
|
97
|
+
return builder.ok(
|
|
98
|
+
"The response body is empty.",
|
|
99
|
+
brief="Empty response body",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
extracted_text = trafilatura.extract(
|
|
103
|
+
resp_text,
|
|
104
|
+
include_comments=True,
|
|
105
|
+
include_tables=True,
|
|
106
|
+
include_formatting=False,
|
|
107
|
+
output_format="txt",
|
|
108
|
+
with_metadata=True,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if not extracted_text:
|
|
112
|
+
return builder.error(
|
|
113
|
+
(
|
|
114
|
+
"Failed to extract meaningful content from the page. "
|
|
115
|
+
"This may indicate the page content is not suitable for text extraction, "
|
|
116
|
+
"or the page requires JavaScript to render its content."
|
|
117
|
+
),
|
|
118
|
+
brief="No content extracted",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
builder.write(extracted_text)
|
|
122
|
+
return builder.ok("The returned content is the main text content extracted from the page.")
|
|
123
|
+
|
|
124
|
+
async def _fetch_with_service(self, params: Params) -> ToolReturnValue:
|
|
125
|
+
assert self._service_config is not None
|
|
126
|
+
|
|
127
|
+
tool_call = get_current_tool_call_or_none()
|
|
128
|
+
assert tool_call is not None, "Tool call is expected to be set"
|
|
129
|
+
|
|
130
|
+
builder = ToolResultBuilder(max_line_length=None)
|
|
131
|
+
api_key = self._runtime.oauth.resolve_api_key(
|
|
132
|
+
self._service_config.api_key, self._service_config.oauth
|
|
133
|
+
)
|
|
134
|
+
if not api_key:
|
|
135
|
+
return builder.error(
|
|
136
|
+
"Fetch service is not configured. You may want to try other methods to fetch.",
|
|
137
|
+
brief="Fetch service not configured",
|
|
138
|
+
)
|
|
139
|
+
headers = {
|
|
140
|
+
"User-Agent": USER_AGENT,
|
|
141
|
+
"Authorization": f"Bearer {api_key}",
|
|
142
|
+
"Accept": "text/markdown",
|
|
143
|
+
"X-Msh-Tool-Call-Id": tool_call.id,
|
|
144
|
+
**self._runtime.oauth.common_headers(),
|
|
145
|
+
**(self._service_config.custom_headers or {}),
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
async with (
|
|
150
|
+
new_client_session() as session,
|
|
151
|
+
session.post(
|
|
152
|
+
self._service_config.base_url,
|
|
153
|
+
headers=headers,
|
|
154
|
+
json={"url": params.url},
|
|
155
|
+
) as response,
|
|
156
|
+
):
|
|
157
|
+
if response.status != 200:
|
|
158
|
+
logger.warning(
|
|
159
|
+
"FetchURL service HTTP error: status={status}, url={url}",
|
|
160
|
+
status=response.status,
|
|
161
|
+
url=params.url,
|
|
162
|
+
)
|
|
163
|
+
return builder.error(
|
|
164
|
+
f"Failed to fetch URL via service. Status: {response.status}.",
|
|
165
|
+
brief="Failed to fetch URL via fetch service",
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
content = (await response.read()).decode("utf-8", errors="replace")
|
|
169
|
+
builder.write(content)
|
|
170
|
+
return builder.ok(
|
|
171
|
+
"The returned content is the main content extracted from the page."
|
|
172
|
+
)
|
|
173
|
+
except TimeoutError:
|
|
174
|
+
logger.warning("FetchURL service timed out: url={url}", url=params.url)
|
|
175
|
+
return builder.error(
|
|
176
|
+
"Failed to fetch URL via service: request timed out.",
|
|
177
|
+
brief="Service request timed out",
|
|
178
|
+
)
|
|
179
|
+
except aiohttp.ClientError as e:
|
|
180
|
+
logger.warning(
|
|
181
|
+
"FetchURL service network error: {error}, url={url}", error=e, url=params.url
|
|
182
|
+
)
|
|
183
|
+
return builder.error(
|
|
184
|
+
(
|
|
185
|
+
f"Failed to fetch URL via service due to network error: {e}. "
|
|
186
|
+
"This may indicate the service is unreachable."
|
|
187
|
+
),
|
|
188
|
+
brief="Network error when calling fetch service",
|
|
189
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
WebSearch tool allows you to search on the internet to get latest information, including news, documents, release notes, blog posts, papers, etc.
|