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,339 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, cast
|
|
8
|
+
|
|
9
|
+
import aiofiles
|
|
10
|
+
import aiofiles.os
|
|
11
|
+
from pydantic import ValidationError
|
|
12
|
+
from pythinker_core.message import Message
|
|
13
|
+
|
|
14
|
+
from pythinker_code.soul.compaction import estimate_text_tokens
|
|
15
|
+
from pythinker_code.soul.message import system
|
|
16
|
+
from pythinker_code.utils.logging import logger
|
|
17
|
+
from pythinker_code.utils.path import next_available_rotation
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Context:
|
|
21
|
+
def __init__(self, file_backend: Path):
|
|
22
|
+
self._file_backend = file_backend
|
|
23
|
+
self._history: list[Message] = []
|
|
24
|
+
self._token_count: int = 0
|
|
25
|
+
self._pending_token_estimate: int = 0
|
|
26
|
+
self._next_checkpoint_id: int = 0
|
|
27
|
+
"""The ID of the next checkpoint, starting from 0, incremented after each checkpoint."""
|
|
28
|
+
self._system_prompt: str | None = None
|
|
29
|
+
|
|
30
|
+
async def restore(self) -> bool:
|
|
31
|
+
logger.debug("Restoring context from file: {file_backend}", file_backend=self._file_backend)
|
|
32
|
+
if self._history:
|
|
33
|
+
logger.error("The context storage is already modified")
|
|
34
|
+
raise RuntimeError("The context storage is already modified")
|
|
35
|
+
if not self._file_backend.exists():
|
|
36
|
+
logger.debug("No context file found, skipping restoration")
|
|
37
|
+
return False
|
|
38
|
+
if self._file_backend.stat().st_size == 0:
|
|
39
|
+
logger.debug("Empty context file, skipping restoration")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
messages_after_last_usage: list[Message] = []
|
|
43
|
+
async with aiofiles.open(self._file_backend, encoding="utf-8", errors="replace") as f:
|
|
44
|
+
line_no = 0
|
|
45
|
+
async for line in f:
|
|
46
|
+
line_no += 1
|
|
47
|
+
if not line.strip():
|
|
48
|
+
continue
|
|
49
|
+
line_json = self._parse_context_line(
|
|
50
|
+
line,
|
|
51
|
+
file_backend=self._file_backend,
|
|
52
|
+
line_no=line_no,
|
|
53
|
+
)
|
|
54
|
+
if line_json is None:
|
|
55
|
+
continue
|
|
56
|
+
self._apply_context_record(
|
|
57
|
+
line_json,
|
|
58
|
+
history=self._history,
|
|
59
|
+
messages_after_last_usage=messages_after_last_usage,
|
|
60
|
+
file_backend=self._file_backend,
|
|
61
|
+
line_no=line_no,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self._pending_token_estimate = estimate_text_tokens(messages_after_last_usage)
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def history(self) -> Sequence[Message]:
|
|
69
|
+
return self._history
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def token_count(self) -> int:
|
|
73
|
+
return self._token_count
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def token_count_with_pending(self) -> int:
|
|
77
|
+
return self._token_count + self._pending_token_estimate
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def n_checkpoints(self) -> int:
|
|
81
|
+
return self._next_checkpoint_id
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def system_prompt(self) -> str | None:
|
|
85
|
+
return self._system_prompt
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def file_backend(self) -> Path:
|
|
89
|
+
return self._file_backend
|
|
90
|
+
|
|
91
|
+
async def write_system_prompt(self, prompt: str) -> None:
|
|
92
|
+
"""Write the system prompt as the first record of the context file.
|
|
93
|
+
|
|
94
|
+
If the file is empty, writes it directly. If the file already has content
|
|
95
|
+
(e.g. a legacy session without system prompt), prepends it atomically via a
|
|
96
|
+
temporary file to avoid corruption on crash and avoid loading the entire file
|
|
97
|
+
into memory.
|
|
98
|
+
"""
|
|
99
|
+
prompt_line = json.dumps({"role": "_system_prompt", "content": prompt}) + "\n"
|
|
100
|
+
|
|
101
|
+
def _write_system_prompt_sync() -> None:
|
|
102
|
+
if not self._file_backend.exists() or self._file_backend.stat().st_size == 0:
|
|
103
|
+
self._file_backend.write_text(prompt_line, encoding="utf-8")
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
tmp_path = self._file_backend.with_suffix(".tmp")
|
|
107
|
+
with (
|
|
108
|
+
tmp_path.open("w", encoding="utf-8") as tmp_f,
|
|
109
|
+
self._file_backend.open(encoding="utf-8") as src_f,
|
|
110
|
+
):
|
|
111
|
+
tmp_f.write(prompt_line)
|
|
112
|
+
while True:
|
|
113
|
+
chunk = src_f.read(64 * 1024)
|
|
114
|
+
if not chunk:
|
|
115
|
+
break
|
|
116
|
+
tmp_f.write(chunk)
|
|
117
|
+
tmp_path.replace(self._file_backend)
|
|
118
|
+
|
|
119
|
+
await asyncio.to_thread(_write_system_prompt_sync)
|
|
120
|
+
|
|
121
|
+
self._system_prompt = prompt
|
|
122
|
+
|
|
123
|
+
async def checkpoint(self, add_user_message: bool):
|
|
124
|
+
checkpoint_id = self._next_checkpoint_id
|
|
125
|
+
self._next_checkpoint_id += 1
|
|
126
|
+
logger.debug("Checkpointing, ID: {id}", id=checkpoint_id)
|
|
127
|
+
|
|
128
|
+
async with aiofiles.open(self._file_backend, "a", encoding="utf-8") as f:
|
|
129
|
+
await f.write(json.dumps({"role": "_checkpoint", "id": checkpoint_id}) + "\n")
|
|
130
|
+
if add_user_message:
|
|
131
|
+
await self.append_message(
|
|
132
|
+
Message(role="user", content=[system(f"CHECKPOINT {checkpoint_id}")])
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
async def revert_to(self, checkpoint_id: int):
|
|
136
|
+
"""
|
|
137
|
+
Revert the context to the specified checkpoint.
|
|
138
|
+
After this, the specified checkpoint and all subsequent content will be
|
|
139
|
+
removed from the context. File backend will be rotated.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
checkpoint_id (int): The ID of the checkpoint to revert to. 0 is the first checkpoint.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
ValueError: When the checkpoint does not exist.
|
|
146
|
+
RuntimeError: When no available rotation path is found.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
logger.debug("Reverting checkpoint, ID: {id}", id=checkpoint_id)
|
|
150
|
+
if checkpoint_id >= self._next_checkpoint_id:
|
|
151
|
+
logger.error("Checkpoint {checkpoint_id} does not exist", checkpoint_id=checkpoint_id)
|
|
152
|
+
raise ValueError(f"Checkpoint {checkpoint_id} does not exist")
|
|
153
|
+
|
|
154
|
+
# rotate the context file
|
|
155
|
+
rotated_file_path = await next_available_rotation(self._file_backend)
|
|
156
|
+
if rotated_file_path is None:
|
|
157
|
+
logger.error("No available rotation path found")
|
|
158
|
+
raise RuntimeError("No available rotation path found")
|
|
159
|
+
await aiofiles.os.replace(self._file_backend, rotated_file_path)
|
|
160
|
+
logger.debug(
|
|
161
|
+
"Rotated context file: {rotated_file_path}", rotated_file_path=rotated_file_path
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# restore the context until the specified checkpoint
|
|
165
|
+
self._history.clear()
|
|
166
|
+
self._token_count = 0
|
|
167
|
+
self._next_checkpoint_id = 0
|
|
168
|
+
self._system_prompt = None
|
|
169
|
+
messages_after_last_usage: list[Message] = []
|
|
170
|
+
async with (
|
|
171
|
+
aiofiles.open(rotated_file_path, encoding="utf-8", errors="replace") as old_file,
|
|
172
|
+
aiofiles.open(self._file_backend, "w", encoding="utf-8") as new_file,
|
|
173
|
+
):
|
|
174
|
+
line_no = 0
|
|
175
|
+
async for line in old_file:
|
|
176
|
+
line_no += 1
|
|
177
|
+
if not line.strip():
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
line_json = self._parse_context_line(
|
|
181
|
+
line,
|
|
182
|
+
file_backend=rotated_file_path,
|
|
183
|
+
line_no=line_no,
|
|
184
|
+
)
|
|
185
|
+
if line_json is None:
|
|
186
|
+
continue
|
|
187
|
+
if line_json.get("role") == "_checkpoint" and line_json.get("id") == checkpoint_id:
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
keep_line = self._apply_context_record(
|
|
191
|
+
line_json,
|
|
192
|
+
history=self._history,
|
|
193
|
+
messages_after_last_usage=messages_after_last_usage,
|
|
194
|
+
file_backend=rotated_file_path,
|
|
195
|
+
line_no=line_no,
|
|
196
|
+
)
|
|
197
|
+
if keep_line:
|
|
198
|
+
await new_file.write(line)
|
|
199
|
+
|
|
200
|
+
self._pending_token_estimate = estimate_text_tokens(messages_after_last_usage)
|
|
201
|
+
|
|
202
|
+
async def clear(self):
|
|
203
|
+
"""
|
|
204
|
+
Clear the context history.
|
|
205
|
+
This is almost equivalent to revert_to(0), but without relying on the assumption
|
|
206
|
+
that the first checkpoint exists.
|
|
207
|
+
File backend will be rotated.
|
|
208
|
+
|
|
209
|
+
Raises:
|
|
210
|
+
RuntimeError: When no available rotation path is found.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
logger.debug("Clearing context")
|
|
214
|
+
|
|
215
|
+
# rotate the context file
|
|
216
|
+
rotated_file_path = await next_available_rotation(self._file_backend)
|
|
217
|
+
if rotated_file_path is None:
|
|
218
|
+
logger.error("No available rotation path found")
|
|
219
|
+
raise RuntimeError("No available rotation path found")
|
|
220
|
+
await aiofiles.os.replace(self._file_backend, rotated_file_path)
|
|
221
|
+
self._file_backend.touch()
|
|
222
|
+
logger.debug(
|
|
223
|
+
"Rotated context file: {rotated_file_path}", rotated_file_path=rotated_file_path
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
self._history.clear()
|
|
227
|
+
self._token_count = 0
|
|
228
|
+
self._pending_token_estimate = 0
|
|
229
|
+
self._next_checkpoint_id = 0
|
|
230
|
+
self._system_prompt = None
|
|
231
|
+
|
|
232
|
+
async def append_message(self, message: Message | Sequence[Message]):
|
|
233
|
+
logger.debug("Appending message(s) to context: {message}", message=message)
|
|
234
|
+
messages = [message] if isinstance(message, Message) else message
|
|
235
|
+
self._history.extend(messages)
|
|
236
|
+
self._pending_token_estimate += estimate_text_tokens(messages)
|
|
237
|
+
|
|
238
|
+
async with aiofiles.open(self._file_backend, "a", encoding="utf-8") as f:
|
|
239
|
+
for message in messages:
|
|
240
|
+
await f.write(message.model_dump_json(exclude_none=True) + "\n")
|
|
241
|
+
|
|
242
|
+
async def update_token_count(self, token_count: int):
|
|
243
|
+
logger.debug("Updating token count in context: {token_count}", token_count=token_count)
|
|
244
|
+
self._token_count = token_count
|
|
245
|
+
self._pending_token_estimate = 0
|
|
246
|
+
|
|
247
|
+
async with aiofiles.open(self._file_backend, "a", encoding="utf-8") as f:
|
|
248
|
+
await f.write(json.dumps({"role": "_usage", "token_count": token_count}) + "\n")
|
|
249
|
+
|
|
250
|
+
def _parse_context_line(
|
|
251
|
+
self,
|
|
252
|
+
line: str,
|
|
253
|
+
*,
|
|
254
|
+
file_backend: Path,
|
|
255
|
+
line_no: int,
|
|
256
|
+
) -> dict[str, Any] | None:
|
|
257
|
+
try:
|
|
258
|
+
line_json = json.loads(line, strict=False)
|
|
259
|
+
except json.JSONDecodeError as exc:
|
|
260
|
+
logger.warning(
|
|
261
|
+
"Skipping malformed context line {line_no} in {file}: {error}",
|
|
262
|
+
line_no=line_no,
|
|
263
|
+
file=file_backend,
|
|
264
|
+
error=exc,
|
|
265
|
+
)
|
|
266
|
+
return None
|
|
267
|
+
if not isinstance(line_json, dict):
|
|
268
|
+
logger.warning(
|
|
269
|
+
"Skipping non-object context line {line_no} in {file}",
|
|
270
|
+
line_no=line_no,
|
|
271
|
+
file=file_backend,
|
|
272
|
+
)
|
|
273
|
+
return None
|
|
274
|
+
return cast(dict[str, Any], line_json)
|
|
275
|
+
|
|
276
|
+
def _apply_context_record(
|
|
277
|
+
self,
|
|
278
|
+
line_json: dict[str, Any],
|
|
279
|
+
*,
|
|
280
|
+
history: list[Message],
|
|
281
|
+
messages_after_last_usage: list[Message],
|
|
282
|
+
file_backend: Path,
|
|
283
|
+
line_no: int,
|
|
284
|
+
) -> bool:
|
|
285
|
+
role = line_json.get("role")
|
|
286
|
+
if not isinstance(role, str):
|
|
287
|
+
logger.warning(
|
|
288
|
+
"Skipping context line {line_no} in {file}: missing or invalid role",
|
|
289
|
+
line_no=line_no,
|
|
290
|
+
file=file_backend,
|
|
291
|
+
)
|
|
292
|
+
return False
|
|
293
|
+
if role == "_system_prompt":
|
|
294
|
+
content = line_json.get("content")
|
|
295
|
+
if not isinstance(content, str):
|
|
296
|
+
logger.warning(
|
|
297
|
+
"Skipping invalid system prompt line {line_no} in {file}",
|
|
298
|
+
line_no=line_no,
|
|
299
|
+
file=file_backend,
|
|
300
|
+
)
|
|
301
|
+
return False
|
|
302
|
+
self._system_prompt = content
|
|
303
|
+
return True
|
|
304
|
+
if role == "_usage":
|
|
305
|
+
token_count = line_json.get("token_count")
|
|
306
|
+
if not isinstance(token_count, int):
|
|
307
|
+
logger.warning(
|
|
308
|
+
"Skipping invalid usage line {line_no} in {file}",
|
|
309
|
+
line_no=line_no,
|
|
310
|
+
file=file_backend,
|
|
311
|
+
)
|
|
312
|
+
return False
|
|
313
|
+
self._token_count = token_count
|
|
314
|
+
messages_after_last_usage.clear()
|
|
315
|
+
return True
|
|
316
|
+
if role == "_checkpoint":
|
|
317
|
+
checkpoint_id = line_json.get("id")
|
|
318
|
+
if not isinstance(checkpoint_id, int):
|
|
319
|
+
logger.warning(
|
|
320
|
+
"Skipping invalid checkpoint line {line_no} in {file}",
|
|
321
|
+
line_no=line_no,
|
|
322
|
+
file=file_backend,
|
|
323
|
+
)
|
|
324
|
+
return False
|
|
325
|
+
self._next_checkpoint_id = checkpoint_id + 1
|
|
326
|
+
return True
|
|
327
|
+
try:
|
|
328
|
+
message = Message.model_validate(line_json)
|
|
329
|
+
except ValidationError as exc:
|
|
330
|
+
logger.warning(
|
|
331
|
+
"Skipping invalid context message line {line_no} in {file}: {error}",
|
|
332
|
+
line_no=line_no,
|
|
333
|
+
file=file_backend,
|
|
334
|
+
error=exc,
|
|
335
|
+
)
|
|
336
|
+
return False
|
|
337
|
+
history.append(message)
|
|
338
|
+
messages_after_last_usage.append(message)
|
|
339
|
+
return True
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DMail(BaseModel):
|
|
7
|
+
message: str = Field(description="The message to send.")
|
|
8
|
+
checkpoint_id: int = Field(description="The checkpoint to send the message back to.", ge=0)
|
|
9
|
+
# TODO: allow restoring filesystem state to the checkpoint
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DenwaRenjiError(Exception):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DenwaRenji:
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self._pending_dmail: DMail | None = None
|
|
19
|
+
self._n_checkpoints: int = 0
|
|
20
|
+
|
|
21
|
+
def send_dmail(self, dmail: DMail):
|
|
22
|
+
"""Send a D-Mail. Intended to be called by the SendDMail tool."""
|
|
23
|
+
if self._pending_dmail is not None:
|
|
24
|
+
raise DenwaRenjiError("Only one D-Mail can be sent at a time")
|
|
25
|
+
if dmail.checkpoint_id < 0:
|
|
26
|
+
raise DenwaRenjiError("The checkpoint ID can not be negative")
|
|
27
|
+
if dmail.checkpoint_id >= self._n_checkpoints:
|
|
28
|
+
raise DenwaRenjiError("There is no checkpoint with the given ID")
|
|
29
|
+
self._pending_dmail = dmail
|
|
30
|
+
|
|
31
|
+
def set_n_checkpoints(self, n_checkpoints: int):
|
|
32
|
+
"""Set the number of checkpoints. Intended to be called by the soul."""
|
|
33
|
+
self._n_checkpoints = n_checkpoints
|
|
34
|
+
|
|
35
|
+
def fetch_pending_dmail(self) -> DMail | None:
|
|
36
|
+
"""Fetch a pending D-Mail. Intended to be called by the soul."""
|
|
37
|
+
pending_dmail = self._pending_dmail
|
|
38
|
+
self._pending_dmail = None
|
|
39
|
+
return pending_dmail
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from pythinker_core.message import Message
|
|
9
|
+
|
|
10
|
+
from pythinker_code.notifications import is_notification_message
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from pythinker_code.soul.pythinkersoul import PythinkerSoul
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True, slots=True)
|
|
17
|
+
class DynamicInjection:
|
|
18
|
+
"""A dynamic prompt content to be injected before an LLM step."""
|
|
19
|
+
|
|
20
|
+
type: str # identifier, e.g. "plan_mode"
|
|
21
|
+
content: str # text content (will be wrapped in <system-reminder> tags)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DynamicInjectionProvider(ABC):
|
|
25
|
+
"""Base class for dynamic injection providers.
|
|
26
|
+
|
|
27
|
+
Called before each LLM step. Implementations handle their own throttling.
|
|
28
|
+
Providers can access all runtime state via the ``soul`` parameter
|
|
29
|
+
(context_usage, runtime, config, etc.).
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
async def get_injections(
|
|
34
|
+
self,
|
|
35
|
+
history: Sequence[Message],
|
|
36
|
+
soul: PythinkerSoul,
|
|
37
|
+
) -> list[DynamicInjection]: ...
|
|
38
|
+
|
|
39
|
+
async def on_context_compacted(self) -> None:
|
|
40
|
+
"""Called after the context is compacted (history is rebuilt).
|
|
41
|
+
|
|
42
|
+
Override to reset internal throttling state when prior injections
|
|
43
|
+
may have been collapsed into the compaction summary and are no
|
|
44
|
+
longer literally present in history. Default is a no-op.
|
|
45
|
+
"""
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
async def on_auto_changed(self, enabled: bool) -> None:
|
|
49
|
+
"""Called when auto mode is toggled at runtime.
|
|
50
|
+
|
|
51
|
+
Override to reset internal throttling state when a mode-specific
|
|
52
|
+
reminder should be eligible to fire again after a user toggle.
|
|
53
|
+
"""
|
|
54
|
+
_ = enabled
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def normalize_history(history: Sequence[Message]) -> list[Message]:
|
|
59
|
+
"""Merge adjacent user messages to produce a clean API input sequence.
|
|
60
|
+
|
|
61
|
+
Dynamic injections are stored as standalone user messages in history;
|
|
62
|
+
normalization merges them into the adjacent user message.
|
|
63
|
+
|
|
64
|
+
Only ``user`` role messages are merged. Assistant and tool messages
|
|
65
|
+
are never merged because their ``tool_calls`` / ``tool_call_id``
|
|
66
|
+
fields form linked pairs that must stay intact.
|
|
67
|
+
"""
|
|
68
|
+
if not history:
|
|
69
|
+
return []
|
|
70
|
+
|
|
71
|
+
result: list[Message] = []
|
|
72
|
+
for msg in history:
|
|
73
|
+
if (
|
|
74
|
+
result
|
|
75
|
+
and result[-1].role == msg.role
|
|
76
|
+
and msg.role == "user"
|
|
77
|
+
and not is_notification_message(result[-1])
|
|
78
|
+
and not is_notification_message(msg)
|
|
79
|
+
):
|
|
80
|
+
merged_content = list(result[-1].content) + list(msg.content)
|
|
81
|
+
result[-1] = Message(role="user", content=merged_content)
|
|
82
|
+
else:
|
|
83
|
+
result.append(msg)
|
|
84
|
+
return result
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from pythinker_core.message import Message
|
|
7
|
+
|
|
8
|
+
from pythinker_code.soul.dynamic_injection import DynamicInjection, DynamicInjectionProvider
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pythinker_code.soul.pythinkersoul import PythinkerSoul
|
|
12
|
+
|
|
13
|
+
_AUTO_INJECTION_TYPE = "auto_mode"
|
|
14
|
+
|
|
15
|
+
_AUTO_PROMPT = (
|
|
16
|
+
"You are running in auto mode. No user is present to answer "
|
|
17
|
+
"questions or approve actions. All tool calls are auto-approved by "
|
|
18
|
+
"the harness.\n"
|
|
19
|
+
"- Do NOT call AskUserQuestion — it will be auto-dismissed with no "
|
|
20
|
+
"answer, wasting a turn. Make your best judgment and proceed.\n"
|
|
21
|
+
"- You CAN use EnterPlanMode / ExitPlanMode normally. They will be "
|
|
22
|
+
"auto-approved. Planning still helps you think before acting; use "
|
|
23
|
+
"it for non-trivial tasks, then exit and execute.\n"
|
|
24
|
+
"- Finish the user's request end-to-end in this run. Do not defer "
|
|
25
|
+
"decisions to a human."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
AUTO_DISABLED_REMINDER = (
|
|
29
|
+
"Auto mode is now disabled. The user is back at the terminal and CAN answer "
|
|
30
|
+
"AskUserQuestion.\n"
|
|
31
|
+
"- Ignore any earlier auto mode reminders that said no user is present or "
|
|
32
|
+
"that you must not call AskUserQuestion.\n"
|
|
33
|
+
"- AskUserQuestion is available again when a decision genuinely changes "
|
|
34
|
+
"your next action. Do not ask routine confirmations or progress check-ins.\n"
|
|
35
|
+
"- Tool calls are no longer auto-approved by auto mode. They may still be "
|
|
36
|
+
"auto-approved if yolo mode remains active."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AutoModeInjectionProvider(DynamicInjectionProvider):
|
|
41
|
+
"""Injects auto-mode (no user present) guidance for the model."""
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
self._injected: bool = False
|
|
45
|
+
|
|
46
|
+
async def get_injections(
|
|
47
|
+
self,
|
|
48
|
+
history: Sequence[Message],
|
|
49
|
+
soul: PythinkerSoul,
|
|
50
|
+
) -> list[DynamicInjection]:
|
|
51
|
+
_ = history
|
|
52
|
+
if not soul.is_auto:
|
|
53
|
+
return []
|
|
54
|
+
if not soul.is_auto_flag:
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
if self._injected:
|
|
58
|
+
return []
|
|
59
|
+
self._injected = True
|
|
60
|
+
return [DynamicInjection(type=_AUTO_INJECTION_TYPE, content=_AUTO_PROMPT)]
|
|
61
|
+
|
|
62
|
+
async def on_context_compacted(self) -> None:
|
|
63
|
+
# Compaction rewrites history; the prior auto-mode reminder may have
|
|
64
|
+
# been summarized away, so let the next auto step restate the
|
|
65
|
+
# constraint.
|
|
66
|
+
self._injected = False
|
|
67
|
+
|
|
68
|
+
async def on_auto_changed(self, enabled: bool) -> None:
|
|
69
|
+
# A runtime toggle changes the latest truth about user presence.
|
|
70
|
+
# Re-arm so the next LLM step can inject the current auto guidance.
|
|
71
|
+
_ = enabled
|
|
72
|
+
self._injected = False
|