diff --git a/.github/workflows/openwiki-update.yml b/.github/workflows/openwiki-update.yml new file mode 100644 index 0000000000..1b48770889 --- /dev/null +++ b/.github/workflows/openwiki-update.yml @@ -0,0 +1,77 @@ +name: OpenWiki Update + +on: + workflow_dispatch: + schedule: + - cron: "0 8 * * *" + +permissions: + contents: write + pull-requests: write + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + # Full history so `openwiki code --update` can diff HEAD against the + # commit it last documented; a shallow clone hides that commit and the + # update runs against an empty change summary. + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + + - name: Install OpenWiki + # mermaid + jsdom are optional; they add high-fidelity validation of Mermaid diagrams. Remove if your wiki has none. + run: npm install --global openwiki@0.5.0 mermaid@11.16.0 jsdom@29.1.1 + + - name: Run OpenWiki + id: openwiki + continue-on-error: true + run: openwiki code --update --print + env: + OPENWIKI_PROVIDER: anthropic + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENWIKI_MODEL_ID: "claude-haiku-4-5" + # Required for the LangSmith connector's code-mode pull to authenticate. + # For extra workspaces, add OPENWIKI_LANGSMITH_API_KEY_2, _3, ... as repo + # secrets and env entries here. + OPENWIKI_LANGSMITH_API_KEY: ${{ secrets.OPENWIKI_LANGSMITH_API_KEY }} + # Optional: also trace this workflow's own OpenWiki run to LangSmith. + LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} + LANGCHAIN_PROJECT: openwiki + LANGCHAIN_TRACING_V2: "true" + + - name: Remove transient OpenWiki run state + if: ${{ !cancelled() }} + run: rm -f -- openwiki/.run.json + + - name: Create OpenWiki update pull request + if: ${{ !cancelled() }} + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 + with: + add-paths: | + openwiki + AGENTS.md + CLAUDE.md + .github/workflows/openwiki-update.yml + branch: openwiki/update + commit-message: "docs: update OpenWiki" + title: "docs: update OpenWiki" + body: | + Automated OpenWiki documentation update. + + OpenWiki result: ${{ steps.openwiki.outcome }} + + When the result is `failure`, this PR intentionally preserves only the + pages completed before the failure. Merge it to make that progress the + baseline for the next scheduled run. + + - name: Propagate OpenWiki failure + if: ${{ steps.openwiki.outcome == 'failure' }} + run: exit 1 diff --git a/AGENTS.md b/AGENTS.md index 588d5c5917..1f51f9b4e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -370,3 +370,16 @@ This repository require actions to be pinned to a full-length commit SHA. Attemp - **Documentation:** https://docs.langchain.com/oss/python/langchain/overview and source at https://github.com/langchain-ai/docs or `../docs/`. Prefer the local install and use file search tools for best results. If needed, use the docs MCP server as defined in `.mcp.json` for programmatic access. - **Contributing Guide:** [Contributing Guide](https://docs.langchain.com/oss/python/contributing/overview) + + + +## OpenWiki + +This repository has a generated `openwiki/` evidence index. It is optional just-in-time context, not required startup reading. + +- Treat source code and tests as authoritative. A brief's unknowns and review items are verification gaps, not automatic requirements. +- Prefer the narrowest quiet validation that proves the changed behavior. Preserve complete failure output. + +The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate. + + diff --git a/CLAUDE.md b/CLAUDE.md index 588d5c5917..1f51f9b4e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -370,3 +370,16 @@ This repository require actions to be pinned to a full-length commit SHA. Attemp - **Documentation:** https://docs.langchain.com/oss/python/langchain/overview and source at https://github.com/langchain-ai/docs or `../docs/`. Prefer the local install and use file search tools for best results. If needed, use the docs MCP server as defined in `.mcp.json` for programmatic access. - **Contributing Guide:** [Contributing Guide](https://docs.langchain.com/oss/python/contributing/overview) + + + +## OpenWiki + +This repository has a generated `openwiki/` evidence index. It is optional just-in-time context, not required startup reading. + +- Treat source code and tests as authoritative. A brief's unknowns and review items are verification gaps, not automatic requirements. +- Prefer the narrowest quiet validation that proves the changed behavior. Preserve complete failure output. + +The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate. + + diff --git a/openwiki/.claims/agent-execution.json b/openwiki/.claims/agent-execution.json new file mode 100644 index 0000000000..3316d68b21 --- /dev/null +++ b/openwiki/.claims/agent-execution.json @@ -0,0 +1,252 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:bd16b5e2d2f508d0aba8962fb1e836569b2a9af1615a3ec3a278948b6b703043", + "claims": [ + { + "id": "claim_601f8ad960a04f2195c35dab22235db1", + "statement": "AgentState is a TypedDict with three fields: messages (a reducer-based list of all conversation messages), jump_to (an ephemeral field for middleware to redirect execution), and structured_response (optional parsed structured output). Messages are accumulated via the add_messages reducer rather than replaced, preserving conversation history across iterations.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L349-L355", + "version": "repo-lines-v1:sha256:44503c793dd9f893d5ba0802082d28f5068e87be55e6e47ed7bf291c25e95709:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNGFjZWQyZjg0ZWIyYWYyZTEwM2QyMGNiMDBjY2ZmYzlmNzI3NmJlMWU3ZTc3ZDdlNzQ2M2U4ZjVjN2RhNjMxNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDU1MDllNzI0MjBmNWM3NGRkY2MyYmE2YmY5NzI2Y2Y4Mjc4MDYzOWIzMTdlNTIzMDY4ODNiN2YyNDkyNjJlMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDcyMjVkNDVlNGY2YmZmZDJlZjYyOTExNjJhNmI3NjM1OTQ1ZmQ0ZTgxZDFmMTk1MGUyYzE2ZDFhOGYxNmY1YSJ9" + } + ] + }, + { + "id": "claim_df5ae204b53d4496bcd30fa5206b8e80", + "statement": "Agent execution uses ModelRequest to encapsulate model invocation inputs (model, messages, system_message, tools, response_format, state, runtime) and ModelResponse to return execution results (result messages list and optional structured_response). Middleware wrap_model_call handlers intercept ModelRequest before invocation and receive the handler callback for retries, short-circuiting, or modification.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L272-L288", + "version": "repo-lines-v1:sha256:14f51ac777a0e4bd6d45fd8fd3d534dd9d72b54a6b2af8d7a71c68f3679ee2b2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjllM2U3YTMyMjI2YmE0MjQ3MmYwMzZkYTU4ZDAzMjFjOTk2MTZmNDRjNGM3OTRmZDQ1YWMwMzQxMzk2OTlkYjYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU2YTM5NTVjMGM0MTE0NDQ1NmRiMGRkMWQ3MGYxNmIxMGRhZjVhMTZhODFmZDBhODFhNmViZjU3YTUxM2M3NGIifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L87-L156", + "version": "repo-lines-v1:sha256:ea2a30a82990ea2528ae5aeb01f676bd28b1b645cbb046365933cb2ea32a0321:eyJzZWxlY3RlZExpbmVDb3VudCI6NzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5YTRmMGQ5MjI5ZGZjYTRhYzUwOWU0NzQ5ZDI0YWJkYjEzODY0YjBhZWYyYjE1ZWJiZDk4OWQ1ODI2OGNkNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzNmRmZDM5MTdiYmM0MGVjY2E5NmYzNWY1NWZmNjJiYTYyMWY0MGI4MDQ4OGUxNDYzN2NiNTY5MjM0NjlmZjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBiMTYxODcxMjE3N2I4YmRjZGZlNmYxYWQ1MTM3MzVmZDMyMGJkMDNlZDYwYmRlNDNmY2JjYWQyNjNiZWJlNWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc1NzdjODAwYzBiNzc4YjA2M2RmN2ZlZmViZmJhNzAyZmI1MzhmMGYyMGJjODFhODFlM2VjNGQwOTI2OTdkYmEifQ" + } + ] + }, + { + "id": "claim_88dd7b40c7054fadad9bdd9b648c3309", + "statement": "ExtendedModelResponse allows middleware to return a ModelResponse paired with an optional Command for additional state updates. Commands are applied via graph reducers, so messages in commands are added alongside (not replacing) model response messages; non-reducer fields are overwritten by later commands.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L213-L261", + "version": "repo-lines-v1:sha256:5404ae4a873489da2373636dee137a925338f578fb6fd654106ce9e3fbcc6aae:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM3YzgzN2QxZjhjOGZmMTQ2ZGFkODY5ZTlhZDhkMGNjYzY2YTA0Y2JhNjVkNzBhYTM5ZDUxMDAwNTQzMzFiNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MjIzMTA2ZmQ4NzQyYjcwZWEyMzNmOWRjMzU5ZjRiMDJiYmIwNjliNzI4M2VlZjc2MjQ5OTUxNmJkN2I5YzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ0ZDAxMGVkNzkyYjIyNTEyNDc4N2ViNzBhZTFlMTQ3ZGNiOGJjMjkxNDgzZTU3ZWRhYWE5M2JkYWJhZmE4ODkifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L290-L313", + "version": "repo-lines-v1:sha256:a9e5563aff24da8a5b4c4b3e197c89e7754e99898de5a58c40bf8ffa63dbcda3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyMTE2Mjk0ZDU5M2IzNjY3MjYwZGVmNDViNTQ2ZDM2ZjkyMGQ3Y2Y5ODU3YWRjODE5NTQyZjBkZDJlMWZlZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI2YzViNjUzOGQ1NDQzNTQ1ZWRkOWY3OTQ5MDMwYTg0MTk3MmZmNjk2MmExMTEwNmIwMjE1YzZiZmM1YTkxNGEifQ" + } + ] + }, + { + "id": "claim_be098dc55d0a4a7b90cbe1d1069b324f", + "statement": "Middleware execute at five lifecycle points: before_agent (once at start), before_model (before each model invocation), wrap_model_call (intercepts actual model call), after_model (after model invocation), and after_agent (once at end). Multiple middleware compose in registration order; wrap_model_call middleware are nested (first = outermost), while before_* and after_* hooks run sequentially.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L431-L501", + "version": "repo-lines-v1:sha256:aa2dbf33279630d1cc44bd246b7c8765d6d98d5c4e289c0010ffeb0a6463989e:eyJzZWxlY3RlZExpbmVDb3VudCI6NzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUxYjY5NWY0NmRmNjM1YzA5NzY1ODQ5NGMzNDkwM2M4YjQ2NDk4YjBlM2FjMjAzNjk4M2JiZDc3MWQzZDBmOGEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkzZTY3YzZlYzBlNjBkY2ViYWQzNjJiYzcyY2U4ZmI0YjdlMmQzMzViODhlOTU2OTAxMjgyNGQ5NTAyZGY4ZDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYyYTk2NjFkOGY1ZjViODU4OTFjNGNkNDg3ZmEyMzhkMGY0YzdmMDk2ZDc2OTAwZTJkZGIzOTY3N2MzNDE3NjgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L503-L597", + "version": "repo-lines-v1:sha256:b4096b631cb7e5cc1f84308854075c32cfa0a013d05971ddb8ac3eef5b777521:eyJzZWxlY3RlZExpbmVDb3VudCI6OTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyNzFhMGExMzM4YjZlODZjZGFiNjVjNjliODU0NzgxNzIxNWJkYjA0M2MxMDY3ZGM0MTU4MmUwMjE1ZWVmZTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmMTYyMTFiOGZmOTg3NDZjYjdjYjVkNmQ0NWU0NTEzODJjZmYxNDU2NzA2YzZiZGMzMGFjMzhkNTY2YjA5ZWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdiNDE3YmIwOWJkYWIwZTgyYmQzYWU5YmM0MDJiMWE0NGM3Nzg2MzJjOWZmZWFjYmE0NDgxMGI0NjZmYTZkYWYifQ" + } + ] + }, + { + "id": "claim_fa90c1d9ff3242fc9f73397074872115", + "statement": "Middleware before_model and after_model hooks can set state['jump_to'] to redirect execution to 'tools', 'model', or 'end'. The @hook_config(can_jump_to=[...]) decorator declares which destinations a hook is permitted to jump to, establishing conditional edges in the graph.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L523-L558", + "version": "repo-lines-v1:sha256:521fe2ea8752360085320e1befb75911a6f1329abdbb431c107b1302b189d882:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4YmI3NTBmNzYxNzkzOWRkMGY4NWQ1ZWE1NDZkZGYzYmE0ZDQzNjcxMTM1ODFhODc0OThhNmM0YWRhNzVkYTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdhMDdiNjdjZmI3YjEzMWE1ZmNjYzgwNTY3Mzc3YTY5MzJmNzM5MTM3MjgwZDgyODU2ZDgxZGQ3OGMxYzdlZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgzYjY1NGJlOWRhZjM3Yzk2ZmM5M2EyMDg2NTdkODM2MGNjMzU2OTA3MTE4MjBhNmI3ODM5ODlmMmVkZjZjMTIifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L879-L900", + "version": "repo-lines-v1:sha256:88672ca5fab7b88c4495126945ec71d455d1f00a74504f8a0fc6ca02cc4542d4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZlNjMxNGFhMjQyMzIyOTE2NjU3NmE3MWI5N2FiYTQ3NTc4ZjI4MDBjMzYyZGIwMDZkNWE4ZmE4MTgwOGUzMjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjIzNWMxMzBmMjdlOTE5MGFjOTUxMzI5NDUxMDE4NWMwYjkzNWFiYTQ0NzI0YzhhYWU3ZmI3MThkYTM0MDNhZGMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJkYWQzMzgyZTlhZjNmNGU4MWFhODM4NjI1ZGNmZjAzNjU0ZWQ1YmNlNzJiZWQyNjRkYTU3ZWQ2NmY0YmVlMWIifQ" + } + ] + }, + { + "id": "claim_1ce1fcc293094ef8b859aa8e00de5326", + "statement": "After model invocation, _make_model_to_tools_edge determines whether to dispatch tools or exit. It checks in order: explicit jump_to directive, presence of AIMessage, tool calls in AIMessage, pending (unexecuted and non-structured) tool calls, and whether structured_response has been populated. It returns Send commands for pending calls or destination strings for exit/loop.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1923-L1975", + "version": "repo-lines-v1:sha256:964403b0cff43101e0a6d7fc69ae5b93dc942efb343cfc9f243a36cbe0684356:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE1MTk5NmZkM2IyMzMzY2I4NjM2ZDA0ODRiZjE5M2FiMTBkM2EzMDIyYTEzNjVjYjUyMWM1OGViZjBlZDU5NWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlY2FhN2I3NjczYjkzMmU5YTQ1M2U2ZGJlNWFiN2E2NTQ4ZTkwYzFkMjdiZWQ0NWI2ZDU2NTUwODNmZTg1NmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUxMTJhMDFhZTM4MjIzNGM5YTBiNzQxOGIxNDJjMGI4MWQ2ODMwODhhMDAzYWVjNDgxZTkyNWYzOTk3NjhjNDQifQ" + } + ] + }, + { + "id": "claim_28399de8706b4de9a4940d95163c6961", + "statement": "_make_tools_to_model_edge decides after tool execution whether to continue the loop or exit. It exits if no AIMessage exists, if all executed client-side tools have return_direct=True, or if a structured output tool was executed. Otherwise it loops back to the model via loop_entry_node.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L2004-L2038", + "version": "repo-lines-v1:sha256:d4d2f05e92f40a9bf4105bce6ae4578bc49a8e2803c4a74c7e3f86cb5c8b91d6:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxY2ZjMmJmMGMwNmFhZmMyOTNjNWE3MTkzYzc2MjFhMmQwYjIyOWZjNGE1MWZjZmRlOGUwYmRiYzUwNjUyMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU4ZTE2NjY1MDcwYTE2MzkwYzYyZjliN2NkNWE2ZWQ5ZmI2ODZhODE2ZGNkNjAwMjBlNTNkMzhmNjAxYzUxYjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRjM2UxMWUyMGMwZTFlZGQ0ZTVkOWQ4NGUxYTI4NjgzNTVlMzhlY2Q5MmRlMWE5OTIyNWRmOGZlN2RiZWZiZDAifQ" + } + ] + }, + { + "id": "claim_054765800b1f4181ab92dd76273329fa", + "statement": "When response_format is configured with ToolStrategy but no regular tools exist, _make_model_to_model_edge governs a loop where the model invokes structured output tools until a valid response is parsed. It checks jump_to, then structured_response presence, and loops back to model if neither condition is met.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1977-L2002", + "version": "repo-lines-v1:sha256:78a3fe672d36eccea386e8f6e817ec5cdeccf57e50ce38238007004ee6655a47:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY3NDNmNWQzM2RhODQ2Y2MwYzJmNmRkMjVkMDMyN2YyNWQ1YWRkZmM1N2UyMWEwMTE2NjY4NDMxZmE2YTE1ODYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRlMTE1ZDU0Njc5ZThkMmFkNzA0YWQ5MzZjMWQwZmRmZGRjMzBjMzNkNDE4NjljMWYzNDE1NzM3YjE5MGIzZDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJjYzA1ZjEyZDMyZmRlYTNmODg3MWI1OWYwNjBhNDMyYjgyMjAyYjI0NmZjNDAzZTg1Yjc5OTg2NDZjZjJkZjkifQ" + } + ] + }, + { + "id": "claim_97e1308250d7487c879b74b73672b82e", + "statement": "The ToolNode executes model-requested tools in parallel when possible. Tools are invoked via wrap_tool_call middleware handlers which receive ToolCallRequest (tool_call dict, BaseTool, state, runtime) and can retry, validate, cache, or skip execution by returning ToolMessage or Command.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1078-L1096", + "version": "repo-lines-v1:sha256:551cee9294f9305a3a7a7c9df2b9b8e501af2a288b6720d879c0f6e56a57b02c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5NmU1NTNmYjUxNmIyMjBjYzVkNTUzODNlNWQxNDYxY2EyODUzOTEyYTRjNWQwNjZiY2NlNjg3ZmYzNDI2MTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0MDZiYjY2MjEzMDVlZGZmMDk1OGU4OWJiN2Y0NTM0ZDJiMTI3ZDIyYjJhYzkwNGU4ZDY1MzZmZjNjYzA5YjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE4YzM2ZmUxMTg0NTI5MDAyNThmZjEwNWEyY2M2NjNlZWJlOWFhN2YxN2YwYzM0NTI3ZWU5NWM0ODdmYTQ4YWQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L674-L755", + "version": "repo-lines-v1:sha256:0c3b22f27f7e6fc59e5c3c949b0f0d9c7f5b5ae8e0ba0541dfe43b06500b90c7:eyJzZWxlY3RlZExpbmVDb3VudCI6ODIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0NTJkY2RlODgxMjZjNGM1Njg1OTMwOGFjNzY3OWU1ODBkYmJiMWNhODNkYjVkYjAxZTJkMTJmMzVhNDdjZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImViZDAyYjcxNTMxYjgxMzNiYTU2NGI0YzRmMzdjM2Q3NTQ4N2U3MWNmOGQ2MDAwYzk4NDFlYThiZTg2YTliNDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZhZWM5OTQwYWQ2NGNjNjJmYWYzZWQzYmMxYmYxZWE3OTQwYWUwMDU2MGEyNTA1YmIxOGM2NmYzZTRmMDc0ZjUifQ" + } + ] + }, + { + "id": "claim_622d8b0f4c3f4b32aefde7cf9ac99a7d", + "statement": "When response_format uses ToolStrategy, synthetic tools are created for each schema. When invoked, the tool call arguments are parsed and validated; if valid, the parsed object goes to state['structured_response'] and the loop exits. If invalid and handle_errors is configured, a synthetic error ToolMessage is injected and the loop continues for model retry.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1222-L1295", + "version": "repo-lines-v1:sha256:68cc95985ab4ccb254f448b23262b2deebd14679410afa69085074f4a8ebd50a:eyJzZWxlY3RlZExpbmVDb3VudCI6NzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM3OGFiMDE1YTZkN2FhZDQ2MWM3NDgzODFjOTkzODIxMjg0MTRiZGYzZWRmNmNkN2Q1YWZkN2IwNDY4YjExNWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjViYTg3NWUwYTNlNzlkYTkwZjdiY2ZhYzYxOWU1ZjU4MWM4MmEwYjU2YzUyMTI3Y2E3YTdmYWM5NzJiNDIxMDYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZmZDk4NGVlZDk1Mzc2NzE5NWQ2MWRmMDk5NTEwNmM2NGJjZmZhZDU0YzMwNjQxYWNhYzA5N2E4Mzk1YjI1MjAifQ" + } + ] + }, + { + "id": "claim_33c65dd1369546b5855d43024cc65c17", + "statement": "Graph entry node is determined by middleware presence: before_agent if present, else before_model if present, else model. Loop entry (after tools) is before_model if present, else model. Loop exit (conditional edges source) is last after_model if present, else model. Exit node is last after_agent if present, else END. Middleware nodes chain in registration order.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1647-L1743", + "version": "repo-lines-v1:sha256:e5d4752fd45563b2294e6d0e37f5aff8f5ced6fd3f7b62a204f170a82d433a0b:eyJzZWxlY3RlZExpbmVDb3VudCI6OTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyYjM1ZjA3NzlhODIwMDkzMTkzZDkzOGEzYWE0OTA1NmVjMjk2ODc2YjViM2E1YWQyZmU1MTA3MDMyMDEzZGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQwOWY2YTA3YmVkNjMwN2Q0MjRjNjAxMzRjMzg3ZThiZjc1NDAxNGEzZjY0ZDk0NzQ3ZWFhZWRkZWViZTMxZTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcxN2UwYTFmMTc1YWZhZGQ2MGViYzMwOGQzMGU4ZDQ3NGExZDVmZGIyMDFlZWZjZWM5NTk0NDRlM2RkZTQ1MDgifQ" + } + ] + }, + { + "id": "claim_6f03a00d76e84d2b9df99159d2c73d41", + "statement": "The agent loop terminates when: (1) model does not call tools (tool_calls is empty), (2) middleware sets jump_to='end', (3) all pending tool calls are structured output tools, (4) a structured output tool is executed, (5) a tool with return_direct=True is executed, or (6) an unhandled exception occurs.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1923-L1975", + "version": "repo-lines-v1:sha256:964403b0cff43101e0a6d7fc69ae5b93dc942efb343cfc9f243a36cbe0684356:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE1MTk5NmZkM2IyMzMzY2I4NjM2ZDA0ODRiZjE5M2FiMTBkM2EzMDIyYTEzNjVjYjUyMWM1OGViZjBlZDU5NWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlY2FhN2I3NjczYjkzMmU5YTQ1M2U2ZGJlNWFiN2E2NTQ4ZTkwYzFkMjdiZWQ0NWI2ZDU2NTUwODNmZTg1NmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUxMTJhMDFhZTM4MjIzNGM5YTBiNzQxOGIxNDJjMGI4MWQ2ODMwODhhMDAzYWVjNDgxZTkyNWYzOTk3NjhjNDQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L2004-L2038", + "version": "repo-lines-v1:sha256:d4d2f05e92f40a9bf4105bce6ae4578bc49a8e2803c4a74c7e3f86cb5c8b91d6:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxY2ZjMmJmMGMwNmFhZmMyOTNjNWE3MTkzYzc2MjFhMmQwYjIyOWZjNGE1MWZjZmRlOGUwYmRiYzUwNjUyMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU4ZTE2NjY1MDcwYTE2MzkwYzYyZjliN2NkNWE2ZWQ5ZmI2ODZhODE2ZGNkNjAwMjBlNTNkMzhmNjAxYzUxYjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRjM2UxMWUyMGMwZTFlZGQ0ZTVkOWQ4NGUxYTI4NjgzNTVlMzhlY2Q5MmRlMWE5OTIyNWRmOGZlN2RiZWZiZDAifQ" + } + ] + }, + { + "id": "claim_5fe06eae05cf4b75a168ebeb41435285", + "statement": "Two strategies for structured output: ToolStrategy adds synthetic tools that the model invokes to provide structured data (works with any model, validates at parse time, slower). ProviderStrategy uses model-native structured output APIs (e.g., OpenAI's response_format, faster, provider-specific). AutoStrategy auto-detects at compile time based on model capabilities.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1347-L1440", + "version": "repo-lines-v1:sha256:c6e24d640bed15caf8ee74c3aa368f689e776daa29654e627127c1279f71ca1b:eyJzZWxlY3RlZExpbmVDb3VudCI6OTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU5YmU4M2VjMDAwMWJjMWVjYWIyNDQ4YWRkY2Q2OTkxNzE5YzJjN2QzZGJkYzgwNjU5ZDU3MzFhYzEzY2ExMzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNmNjYwZDcxNGYwOTczZDFkM2M4M2YzNTM5YTZjNjMyYjllNjU0ZTA5NTIzNGI1ZDQxNzc3OWFjY2QyNTg4ZTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhlYmExODRkNzU3NmZjZWUwMWEzYmRiNWVjZDI5N2IxYjQxODExYWUxYzI0YmExOGIzZjE0YmM2NGE1ZDA2M2YifQ" + } + ] + }, + { + "id": "claim_6af6572eef85470e9fd0c6c67a04f69a", + "statement": "The messages field uses add_messages reducer, so returned Commands with {\"messages\": [msg]} append msg to the list rather than replace it. This preserves conversation history across iterations and ensures each model invocation has access to the full conversation and all tool results.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L237-L243", + "version": "repo-lines-v1:sha256:90c2906591d5390e6dcbbc5e41b564f6f3d87e9476efdc5127ac996ebd3db382:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiN2RmMTA3ZmZiOWMzNzBiZjM1MTdhNzM5NTJiMDBmMmE2OTUyNDdiNmY5ZDNmZWQ0ZjIwYjRjZDkxZmIzMWMwZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDNkMzk2YzQ2Yzc1Mjc0NWRkMDRjMzM0MjdjYTE3NTk3MzBhMjcwODg3YWRmZDI0NjM2ZDM1NGI3NTQwNDY2NiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDdmMmM3NjdjZWUyZTMyZDY3N2Y5MWY5ZThkZDY1NTZkODBiNGRkZWZkYTc0MGQ5NjZiNjk1ZTExZDc0NmU2MCJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L349-L355", + "version": "repo-lines-v1:sha256:44503c793dd9f893d5ba0802082d28f5068e87be55e6e47ed7bf291c25e95709:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNGFjZWQyZjg0ZWIyYWYyZTEwM2QyMGNiMDBjY2ZmYzlmNzI3NmJlMWU3ZTc3ZDdlNzQ2M2U4ZjVjN2RhNjMxNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDU1MDllNzI0MjBmNWM3NGRkY2MyYmE2YmY5NzI2Y2Y4Mjc4MDYzOWIzMTdlNTIzMDY4ODNiN2YyNDkyNjJlMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDcyMjVkNDVlNGY2YmZmZDJlZjYyOTExNjJhNmI3NjM1OTQ1ZmQ0ZTgxZDFmMTk1MGUyYzE2ZDFhOGYxNmY1YSJ9" + } + ] + }, + { + "id": "claim_5f38e06f65fc4c50b3a280406d6da207", + "statement": "The agent graph is compiled with recursion_limit=9_999 to allow very long agent loops (hundreds of tool calls) while still protecting against infinite loops.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1829-L1835", + "version": "repo-lines-v1:sha256:77c3c4c9a7eec5a3838f8f7fe0a5efba7f3a7cc85d27ef29572a4e8a0e4af9e6:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZWJjMjYxY2MzOWJmZGZkMzA4NDg5NjliMWE3YjNhNmZhZDk4ZjkyMzIwMzY5YjBiOTliNGE5YTNhNjg5ZjIzNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTUwNWRlZDFlOTExNGE3OTdhYmFlYTExNjAxOWE3YWY2YmUwNTVlMTRjMzA4ODg4NTM4MTAwMjhlYmE3NjM0ZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGUyYmM1Mjc1MTNkMTkzYTE5OWNiZGM4YTBhNDUxMzdlMzVjMGNkZTc5M2NmMWE1NThmNWNmYmM1MGI4MDZjMCJ9" + } + ] + }, + { + "id": "claim_0f2bbaa83f4a4dd9b9cff4cdeceeea21", + "statement": "The model_node constructs a ModelRequest from current state, invokes wrap_model_call middleware if configured (passing _execute_model_sync as handler), and returns Commands that update messages and structured_response. Async variant amodel_node is provided for async-capable models.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1468-L1543", + "version": "repo-lines-v1:sha256:54cd45424a6da345b6cdd58f06adae623405b4dd53cb45edf97323f1d2b57833:eyJzZWxlY3RlZExpbmVDb3VudCI6NzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxNmM1NDhlYTczZDQ4MDYwYjczZTVkODUwYjE3MWRlNjVjZGZmNWUyYTdhMTg2ZTY0MmEwZWEzMWJmYmNhZTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcwNzE5M2JhNTk1MzA3ZjRmYzk2YTY5ZTZlY2MyMDc5ZmUwMmM4OTdlNGVjOGU3NTI0YmQzYzE3NmU0MmFhYjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU3YWEzMjYzMTc0YzhiNjQ4ZTI4YmIxMWI5MmZkYTQ0MGUyMTlkZDIzNzU5ZjhmMTA4MTliZDk4MWQ0OWZhNzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE5MjZmNGU5Y2ZmODIyNjdmZDg3MTEzN2E3ODg3MTY3NTVmYTg5ZTg1YTRkZGFjZTFiZWU0ZjY0NDRhZmJjYmQifQ" + } + ] + }, + { + "id": "claim_331e129b7a2847ea867de91e4906c897", + "statement": "_chain_tool_call_wrappers composes multiple wrap_tool_call middleware into a single middleware stack where first-registered middleware is outermost. Each wrapper receives the request and a handler callback for the inner layers, enabling retry and interception patterns.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L658-L768", + "version": "repo-lines-v1:sha256:9f698f73bbdcf575280c38a0fafe57eb6ca684fa2347f96d8b7e425f9b415462:eyJzZWxlY3RlZExpbmVDb3VudCI6MTExLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJlNjg3ZGI4NmIyYjJmZTY0NTgxZWRhMTFiMTUxMTQ0M2UwMWY4MTI4MzM4YmFlODg2OTQ0NWRhOGQzODhlZmQzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0ZWU1ZGVjNGMyZTc5MDBiMzFlOTdmMDNjYjBhMjljNDliOWQzYmZmNjQ2YTdhNDc2MmMyOTc0NzlmYjNhOWY4IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJkZjNlYjVkMmNjYTU1Nzc5ZGM2ZDgwNDU5YjEyYjJiNTY4OTIzMWIyMzlkMTczZTM2Yzk3MDhlZDc4MWNjMTMwIn0" + } + ] + }, + { + "id": "claim_bb8a178791b941109a2246cab91e4b8c", + "statement": "_chain_model_call_handlers composes multiple wrap_model_call middleware into a single nested handler stack. Commands from each layer are accumulated in inner-first order and merged via _build_commands without merging individual commands.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L263-L352", + "version": "repo-lines-v1:sha256:272c0933c5414b8cecc2e64643c365fa65e76088a91d385cdab443f37076f035:eyJzZWxlY3RlZExpbmVDb3VudCI6OTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1Y2IwNmM0MzdmOTk3ODhiZDVjOGM4MDY5MjU0MTI5YmVlN2ExNGYzMzc2ZTFjZWY2MTczYWUyMGE3Y2YyZjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViZjVlNjBjNjUyNDc1YjVjNzhmMzdlYmI2OTJjNWIxM2NhZDMwMjI4NmM3MDFlYzBhZTAxODVhMWZiMzVjZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI4YjE2YTdhZjFkZDFlYzI4OTY3MWRhMWU5MmExY2UzN2I4MTA0YWFlYjZlYjU3MzY1NmU4NWVhMzU0YTNhY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU5YTcwM2FiYzE5MWZjZWUxOWIwMzM5NTc3MjEzZWQ2OTMyMGRlOTFmZWMwNDdjZjAzYTY0NTUyYzNiYTY1NjQifQ" + } + ] + }, + { + "id": "claim_15a9686f4ca24089a72a76df096e2c3a", + "statement": "Agent state schema is resolved by merging state_schema (if provided by user) with middleware state_schema attributes. Later schemas override earlier ones when fields conflict. This allows middleware to extend state with custom fields while user-provided state_schema has highest priority.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1174-L1182", + "version": "repo-lines-v1:sha256:79c59a206dc67c41cff7e2895b104673b0bf154adb4e716a7f0eb051aa756b95:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiY2JkZjE5OGNiYzcwODIzNWMwMTEzMzdjZjY0MzZlNjE3MTc5MDAzODg4MjA4NzY2NTY2ZmFkMGU1MGY1M2FiZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMWU4OGFjNGI4NWI5N2FiYWUwZWRjYTg2MDcyM2U3ZDE2M2M5Yzc5NDQ1NDVhM2M3YzViNzNkOGZhZDdlYzg3YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMmFhN2Q3ZTQ5YTE5ZTM4NjhlODE2OWY0ZDk3ODA5NzlkMjYwZTAxODkxYjk4MTJjODhiYjEzNzU1ZmMwZTEyYSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjJmMzA1NDI5M2U4OWU5ODE1NDRkYmNmMjdiNjY5YzhkNzA5YTNmOThhN2U1NDQxZDA1OWIyNmJkNjIyOWU1MSJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L452-L468", + "version": "repo-lines-v1:sha256:95231fc24c27b9e2345a191bbd3daa928421c5f14d8bdc0350fe29794a71adf1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjMzliNTU0NjBlOTk3Y2YxYzM0NTIzYjY2ZjgxYzhmODNmZjg2YzRiNDhlNWE4MmRjMTYwNjk5NzQzOTk2ZTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVjNWI4NzFjNWZhNDJkMDViMWZjMDc5ZjY3MDg1OWM4YzFjMTZhOWU3NjRiNWI0OTYwYTM1MWE4NzBlNmIzYTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIwNzJlMDQ5YjI4OWQzZGFlMzUzMTM5OTdhZTYxY2Y5NDlhMmNmOWQwYmVmMDQ5OTMwYzEyN2VlYjk0ZGFiNDYifQ" + } + ] + }, + { + "id": "claim_ffd7448536a446eaa99cb79dc8171180", + "statement": "_get_bound_model binds the model with tools and response_format configuration. It performs auto-detection of structured output strategy if needed, validates that middleware-added tools are registered or have wrap_tool_call handlers, and returns the bound model and effective response format.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1298-L1440", + "version": "repo-lines-v1:sha256:a9262c2fd1ed4c970da50f7898b4bb2a19482839aef74c97348f9f14383141b6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI4ZjU5YzFiMTk4OWE4MWFiOGFlOTg1NzI5MDJmYTdlOTE3NjcyM2Q3ZDJmNzMzM2I4YzUzZWRhOTE5YWQzMDIxIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxZmVkNjE1ZWZhMTFmMGE2MmUwYmZkNjA2YWQxNTZjNzVlNjU2MTUwZTFmMmZjZTg5MGY4MWI1ZTI5NWFmOTg1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI4ZWJhMTg0ZDc1NzZmY2VlMDFhM2JkYjVlY2QyOTdiMWI0MTgxMWFlMWMyNGJhMThiM2YxNGJjNjRhNWQwNjNmIn0" + } + ] + }, + { + "id": "claim_92414361d4574c2283188893067eddef", + "statement": "Model invocation exceptions propagate unless wrap_model_call middleware catches them. Tool execution exceptions propagate unless wrap_tool_call middleware catches them or ToolNode.handle_tool_errors is configured. Structured output validation errors can be configured to retry or raise via response_format.handle_errors.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L628-L656", + "version": "repo-lines-v1:sha256:00fd5f0b9df0762ae54ede2d60f5bbeb785b3055f04eeebf87c015e0171088de:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZmM2JjYWUzMzhlZjQxNGQ2NmFhM2Q4MTUxNjA5MmE2MWMxNGZhNTllZGExMTMxMmVlMzgwM2UwNDNjZDAyMzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA5ZjViNTk4NTM5MWU3MTAyMWY1ZDZiYjczZTE1YzgzZDYwNmJmNTNhMTVhYzM1NGRhMGI0OWM3YzZjYWZlM2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM5OWMwMTM5N2U5ODE1NGMzY2ZmMDZlZWY2MzhmNjAwODc0NjA1NTA0OTUzNTJlMmQwMDFkMTU4OThmYmUxODEifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/agent-factory.json b/openwiki/.claims/agent-factory.json new file mode 100644 index 0000000000..f09ceabb41 --- /dev/null +++ b/openwiki/.claims/agent-factory.json @@ -0,0 +1,230 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:861e60569112cc57094912804e603c9cd7bd4162df709645dd543bb46b2cca3f", + "claims": [ + { + "id": "claim_0d3a898647bd4d1d9c08eb386919394b", + "statement": "The create_agent function is the primary factory entry point for building agents. It accepts a model (string or BaseChatModel instance), optional tools, system prompt, middleware, response format, and state schema, and returns a compiled LangGraph StateGraph that implements a model-tool loop.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L840-L993", + "version": "repo-lines-v1:sha256:95ec9d7e14e22e1dc6f068eaa3246a9290e730715df1cd510e1445b1b17cd69d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTU0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkNjg5OTkzM2ZhODgwYTBlZjI2YzJhOGI0OTAwZTM3MTc0Nzg4YWRiNWFlYjA3YmVkNTczMjY0MmIwNDU1ZTBlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhNmE3MTUyNTcyZWU4NGYxMDkwNmM3NGI5ZjJhOTI0MzRkZGFiMGUzYzQyODEzYjA0ZTU5N2VmY2VjOTUxYjI2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJkNjBmZTg2NTMxMGRlMzMwZjE2YzQyZTY4ZjMwMWMzODNhZjg3MDk2NzVhYjYyN2E0OTcxNmY0YzQ2OWFiOGU0IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI0ZjE2MTA3ZTE5NjcwOTVkMDA0NTZmNTFkMzc1ZjYxYTQ1ZjkyN2U4YjAyOGRlMGZlNDUyMGIzNzM4OWMyMTU0In0" + } + ] + }, + { + "id": "claim_6d5428a93f294d5daf8a181cdd80d95f", + "statement": "The agent factory constructs a state machine with nodes: START → before_agent → before_model → model → after_model → [tools → after_model loop | after_agent] → END. The model is called, and if it returns tool calls, the tools node executes them and loops back unless a tool has return_direct=True or a structured output was produced.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1647-L1743", + "version": "repo-lines-v1:sha256:e5d4752fd45563b2294e6d0e37f5aff8f5ced6fd3f7b62a204f170a82d433a0b:eyJzZWxlY3RlZExpbmVDb3VudCI6OTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyYjM1ZjA3NzlhODIwMDkzMTkzZDkzOGEzYWE0OTA1NmVjMjk2ODc2YjViM2E1YWQyZmU1MTA3MDMyMDEzZGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQwOWY2YTA3YmVkNjMwN2Q0MjRjNjAxMzRjMzg3ZThiZjc1NDAxNGEzZjY0ZDk0NzQ3ZWFhZWRkZWViZTMxZTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcxN2UwYTFmMTc1YWZhZGQ2MGViYzMwOGQzMGU4ZDQ3NGExZDVmZGIyMDFlZWZjZWM5NTk0NDRlM2RkZTQ1MDgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1923-L2037", + "version": "repo-lines-v1:sha256:a9bc359f6e804a4cdc0cb76ebfb4141003353d4d8539636a2a7f5f658cb4c82c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTE1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIxNTE5OTZmZDNiMjMzM2NiODYzNmQwNDg0YmYxOTNhYjEwZDNhMzAyMmExMzY1Y2I1MjFjNThlYmYwZWQ1OTVlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJjNDQ3OWNjZWQ2NmFmZmMzYzA5OTg4YjUwMzg2NTZjYTUyYTlmOWE0NDUyYzFhNGY2ZWU0NWQ1ZWM1YjQ1N2EzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI4ZWNhYTdiNzY3M2I5MzJlOWE0NTNlNmRiZTVhYjdhNjU0OGU5MGMxZDI3YmVkNDViNmQ1NjU1MDgzZmU4NTZmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIxM2EwMTI5M2NiNzY5ODQyNTU5NDM1YmM0NmRmYzUxYWEwYWZjZTFhM2IwNDcxNGQzYjE3ZTMwNmM3YjI0MWUwIn0" + } + ] + }, + { + "id": "claim_669df27b0706415ebb76513506ecef21", + "statement": "AgentState is a TypedDict with three fields: messages (list of conversation turns, uses add_messages reducer), jump_to (ephemeral field for routing overrides), and structured_response (optional parsed structured output, cleared each iteration unless set).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L349-L354", + "version": "repo-lines-v1:sha256:c995df23d29c623277091b5c9a18504b94c75d83702467cccbe504b1ac99b777:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNGFjZWQyZjg0ZWIyYWYyZTEwM2QyMGNiMDBjY2ZmYzlmNzI3NmJlMWU3ZTc3ZDdlNzQ2M2U4ZjVjN2RhNjMxNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMGIzOTE4NzQ3OTQ1N2E1YjMzYjc0ZWU2Yjk2YjM4OWFmYWUwYTYyZmRjMDkzMjljZjhhZTgyMWQzNDc3MmE1ZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDU1MDllNzI0MjBmNWM3NGRkY2MyYmE2YmY5NzI2Y2Y4Mjc4MDYzOWIzMTdlNTIzMDY4ODNiN2YyNDkyNjJlMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZWE2ZDczZWY1MTNjNDcwNmU4YzA5MDNhOWI4OTdmZmQxZDdlYTU1NjNhMDYzZWQ5NTJlNThjMDM3NmQ4NWZjNiJ9" + } + ] + }, + { + "id": "claim_cbe2cd0b74f5418499340266846f0044", + "statement": "ModelRequest encapsulates all inputs needed for a model invocation: model instance, messages, system_message, tools, response_format, state, runtime, tool_choice, and model_settings. Middleware access and transform requests immutably via request.override(). ModelResponse carries result messages and optional structured_response.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L272-L287", + "version": "repo-lines-v1:sha256:4b29b35e7cd99402231b6a07024efadb326a7c5048754fc1b27ace7a459970f7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4NzBmYWYzYzFkNDkwMGI1ODNlZGI3MjYxNzk1YWFiZjZmZDI2NWZjMDllNjliMDQwNmRlNzMyMmQ3ZTMwMDciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjllM2U3YTMyMjI2YmE0MjQ3MmYwMzZkYTU4ZDAzMjFjOTk2MTZmNDRjNGM3OTRmZDQ1YWMwMzQxMzk2OTlkYjYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZjMWYwMmZkN2VjMWQ0NjJjMGNiODE4Yjk0OGZmZTRmNzE1YjJjMmM0MzI2MzBjMjMyZGZlMzRkNDI3NDNmYTkifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L87-L269", + "version": "repo-lines-v1:sha256:2e092d648db88d84598f61d1f81e0d629676c59f2cd9d2892492f2a466327fb3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmOWE0ZjBkOTIyOWRmY2E0YWM1MDllNDc0OWQyNGFiZGIxMzg2NGIwYWVmMmIxNWViYmQ5ODlkNTgyNjhjZDUzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1NzZhMTRjYWViNDY3MTJkNzBjNmJmOWEyYzhkNWNiZTY4NzdjOTJmMWM3YmFkNGQ2MjA3ZTNiZmUwYzNkN2FiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIwYjE2MTg3MTIxNzdiOGJkY2RmZTZmMWFkNTEzNzM1ZmQzMjBiZDAzZWQ2MGJkZTQzZmNiY2FkMjYzYmViZTVmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2YzFmMDJmZDdlYzFkNDYyYzBjYjgxOGI5NDhmZmU0ZjcxNWIyYzJjNDMyNjMwYzIzMmRmZTM0ZDQyNzQzZmE5In0" + } + ] + }, + { + "id": "claim_56b580a488d6440191895a645b58ac2f", + "statement": "Middleware are composed into stacks at each hook point. First middleware in the list becomes the outermost layer. For wrap_model_call hooks, this means the first middleware is called with a handler that wraps all subsequent middleware. Sync and async paths are kept separate; if only async is defined, sync invocation raises NotImplementedError; if only sync, async falls back.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1138-L1172", + "version": "repo-lines-v1:sha256:575ccc0a21975b18543d9fc9e06ef05259be9eaa35c64b06891a47f0e20a1fca:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMxMjAyNWJlYjMzMmNhZTY1YzgzOTI2ZGNkYjRmNTEyZjYyYjk1M2Y5ZWNhN2ViYjdmN2UyZjQzNWQ5NzRhYmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg5YmE3M2IxNzM2ZjRjMjJlZGI1OTJlM2VhOGExYTM3MmNhNGQ3MDM0ODEyMWFlOGIxNjA2MDFlNDE1Y2FhMDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZlMjA2MDAzYTAzYzM3M2IzMzg4NWViNGE5ZjJlYzgzNmMxZjIwZDFhNjVkZjEyNWMyOWY1Y2M0MTRmMDUzN2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlOGE2NWIxZGMzMzM0NWExYTY4MWNlYjE3OTJiNzhhNGVlNGM4YTk2ZjBhMjcwYTAyMTY2NmZjYmVlYTE1OGQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L263-L352", + "version": "repo-lines-v1:sha256:272c0933c5414b8cecc2e64643c365fa65e76088a91d385cdab443f37076f035:eyJzZWxlY3RlZExpbmVDb3VudCI6OTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1Y2IwNmM0MzdmOTk3ODhiZDVjOGM4MDY5MjU0MTI5YmVlN2ExNGYzMzc2ZTFjZWY2MTczYWUyMGE3Y2YyZjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViZjVlNjBjNjUyNDc1YjVjNzhmMzdlYmI2OTJjNWIxM2NhZDMwMjI4NmM3MDFlYzBhZTAxODVhMWZiMzVjZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI4YjE2YTdhZjFkZDFlYzI4OTY3MWRhMWU5MmExY2UzN2I4MTA0YWFlYjZlYjU3MzY1NmU4NWVhMzU0YTNhY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU5YTcwM2FiYzE5MWZjZWUxOWIwMzM5NTc3MjEzZWQ2OTMyMGRlOTFmZWMwNDdjZjAzYTY0NTUyYzNiYTY1NjQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L503-L596", + "version": "repo-lines-v1:sha256:685b54e44e0b63050d5c533c11d327da4c318fadf8aa58fdaacc4f987c989fb5:eyJzZWxlY3RlZExpbmVDb3VudCI6OTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyNzFhMGExMzM4YjZlODZjZGFiNjVjNjliODU0NzgxNzIxNWJkYjA0M2MxMDY3ZGM0MTU4MmUwMjE1ZWVmZTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzM2IzNjc5NjdlODc3ZWJhMmRhMTZmNDBmYjljMDgwYzAwZjM3MmNmODE2ZmU2N2MxNjczMWNmMzJlN2FhNGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmMTYyMTFiOGZmOTg3NDZjYjdjYjVkNmQ0NWU0NTEzODJjZmYxNDU2NzA2YzZiZGMzMGFjMzhkNTY2YjA5ZWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzc1NjhmMzcxYzY2NGZhNzM5ZmU2ZGYzNzAyZTc2MjUzZTA3NDMyOGQwMjU2YTRhYWQzZGVkOTlkN2Y4YTQifQ" + } + ] + }, + { + "id": "claim_443899ae94104dac9e009c65d380d539", + "statement": "AgentMiddleware defines six hook methods: before_agent (once at start), before_model (each iteration), wrap_model_call (wraps model invocation for retry/fallback/caching), after_model (each iteration), wrap_tool_call (wraps tool execution), and after_agent (once at end). Each has async variant prefixed with 'a'.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L385-L650", + "version": "repo-lines-v1:sha256:6a17f1a47e3d2e7b75302dc1b1f20a9329f7ff647b776f89819b6da5a4423ccf:eyJzZWxlY3RlZExpbmVDb3VudCI6MjY2LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkYTZiYjZmZDM3N2VmMGEwZDlmMDZkZmYyNGNjYjNhMmI1NzY1ZTA5MDY5NTExNmU4MzBjOTAzYTdjMmQ3MmMwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiZWRhNDEzZTc5NTEwYTU5ZTM1ZWJiMTcyYjZiMWM0OWVkZWI5OGNlZDA5YTYzODZlNzg1YTNiOWZkMzJhMWUyIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI2NmJhYThhZGNkYmI0M2JiMTdiNzU1ZTE4MDgxNGY5MzM2NjEyYThkMTEwMTdmMmY1MTZlMWUyYzIzNTExYzQyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNGRmZDBjMGIwMTczYWQxNWEzMmNhY2YwNmY0ZjdjNGI0NDM0OWNlNmMyZjFmYzYwNDA3YjgxNDQ4MWMxNTcyIn0" + } + ] + }, + { + "id": "claim_096b32594b4244bea14d42ecc92a1b9a", + "statement": "The factory supports three structured output strategies: ProviderStrategy (model's native structured output, auto-detected via profile), ToolStrategy (synthetic tool call), and AutoStrategy (raw schema that auto-detects best strategy). When enabled, structured output tools are synthesized and added to the model binding; tool calls matching them are parsed and remove the need for further tool execution.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1010-L1036", + "version": "repo-lines-v1:sha256:46ca7abe64408d9241323b742fd554652a4ee0181fe59e35bf62c09fb5599f2d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ2ZTA0NTJmNzY0NWFhYjhiN2UxZDg5NTk5MTVmOTc4NDRkODIwOWVmOTJiZDVmZjQwNDE1Y2QwNjc4ODBmNWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyMjZhNDMzM2Y3ZmRmODhhYmNiOTQ4Yzk2YmY4Mjc5ZWQ2NzE4ZDYxNTVlZjFmZDRiMzQ2MjdjNzMyYTEwZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIyNjM3MDgzNzA0N2VkYzNmOTZlNTY1MjI5NjZiZWFhYTExYjM5YTEyYTg0ZWZmZWZjYWNhMWFmN2ZmNWQ1ODIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUwZDdkNjYyMDI4NTk1ODI5Y2RiM2U4OWZlZjI5ZDczMjYxY2ZkNTVlZmUxNTdjNDJmZmMxNWQ1ZTA2ZjQ0OWEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1194-L1296", + "version": "repo-lines-v1:sha256:42ca8ea665535b713f1ff2651387e04941608ec55a949c4f1cf00b8febc38c66:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyYmRmNGQzMWNjY2QxYzBjYmY1ZWRkZjNlZWMxNDZlNDUwYjExMTdhNjJmZGExZDlhMzBkYTBjMzI2YTI2YTJiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyYzkyOTk3NTc2NmYwNzM5NTNlNTA5YzIyYjI0ODBiNjE0NjQzN2FhZmIzMmYyOWJkNTJjMGM4M2E1Y2ViZjU5IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmZjc4ZDc2OWUzNzBlMjA1NmU3MmI2YTU1OTkzMWIzYzVhODNjMGE2YWI2YWFmYjhmZTkzOGE3NTY3Y2Y4ZDNkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5ZjgxNDM1OTIzMTAwNThkODUzZTY3NzM5YTExNzY2NTkyNjdhODM1MTIxYTc0OWI5Nzk1ODU2M2UyZWU3MmVlIn0" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1347-L1439", + "version": "repo-lines-v1:sha256:f84cbce24d4d24d64ce80d62a745f206aecb577ee514e94a432b4195177be749:eyJzZWxlY3RlZExpbmVDb3VudCI6OTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU5YmU4M2VjMDAwMWJjMWVjYWIyNDQ4YWRkY2Q2OTkxNzE5YzJjN2QzZGJkYzgwNjU5ZDU3MzFhYzEzY2ExMzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU1YTgwNTQwYzMzY2M2Zjk2Y2EzMmU1ZmQ3NDgyMWMyYjZlYzEyM2EyZjRkZTcxMTYyZGQ3YmE3NzA4YjkyZTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNmNjYwZDcxNGYwOTczZDFkM2M4M2YzNTM5YTZjNjMyYjllNjU0ZTA5NTIzNGI1ZDQxNzc3OWFjY2QyNTg4ZTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE2ZmQ0MzA2NzZkMTI4Mjc4ZmQxM2FhMTdmNDk4Yzc0MzExNmM4OGRmZmIxNGI4MTdlZTZjYzNhMmU2MDE1OWYifQ" + } + ] + }, + { + "id": "claim_d24918c8f37043feb309aa60372f33ca", + "statement": "The factory merges state schemas from middleware (in registration order) and user-provided state_schema (if any). Later schemas override earlier ones on field conflicts. This allows middleware to extend state without forcing users to declare all fields, while letting users override middleware definitions.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1174-L1182", + "version": "repo-lines-v1:sha256:79c59a206dc67c41cff7e2895b104673b0bf154adb4e716a7f0eb051aa756b95:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiY2JkZjE5OGNiYzcwODIzNWMwMTEzMzdjZjY0MzZlNjE3MTc5MDAzODg4MjA4NzY2NTY2ZmFkMGU1MGY1M2FiZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMWU4OGFjNGI4NWI5N2FiYWUwZWRjYTg2MDcyM2U3ZDE2M2M5Yzc5NDQ1NDVhM2M3YzViNzNkOGZhZDdlYzg3YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMmFhN2Q3ZTQ5YTE5ZTM4NjhlODE2OWY0ZDk3ODA5NzlkMjYwZTAxODkxYjk4MTJjODhiYjEzNzU1ZmMwZTEyYSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjJmMzA1NDI5M2U4OWU5ODE1NDRkYmNmMjdiNjY5YzhkNzA5YTNmOThhN2U1NDQxZDA1OWIyNmJkNjIyOWU1MSJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L452-L506", + "version": "repo-lines-v1:sha256:153d997320fe740d99c810085479142edca654cc31fb26d9e04ac0f4d056aeed:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjMzliNTU0NjBlOTk3Y2YxYzM0NTIzYjY2ZjgxYzhmODNmZjg2YzRiNDhlNWE4MmRjMTYwNjk5NzQzOTk2ZTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVjNWI4NzFjNWZhNDJkMDViMWZjMDc5ZjY3MDg1OWM4YzFjMTZhOWU3NjRiNWI0OTYwYTM1MWE4NzBlNmIzYTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU4M2M2YjE4NWUzZWZiOGMxYjYzYTYyMjI2OGJmNTYxZjViN2ZmOGIwYWQ5NDJkMTk5MDUxMmMyOTJlMmQ2MDEifQ" + } + ] + }, + { + "id": "claim_0acc06eb9c7d45e8b886f43ee4a296b7", + "statement": "Tools are registered at agent creation from BaseTool instances, raw callables (auto-wrapped), or dict-based provider tools. The ToolNode batches pending tool calls and executes them. Conditional edges route back to model unless a tool has return_direct=True, a structured output tool was called, or no tool calls remain.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1077-L1096", + "version": "repo-lines-v1:sha256:f51ce8995c6e7b5b534cc0b32939c94c0739cfdae2e1bac61c23707201372310:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkxN2EyMDA1ODk0MzI1ODA1MmVlOTkzNDZjZmQwNGI0OTVmOWM4ZGI5NDMyMjMzYTk3ZWMxNWI5ZGM3ZDZhOWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFiZmE4NzY1YjhjZDgxOWNlZDI4ODVkMzFmMTNkNTUwMGUwYWJiMjdhZGM1M2FlOWM3ZDE5YTJmZTNhNDFkZTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE4YzM2ZmUxMTg0NTI5MDAyNThmZjEwNWEyY2M2NjNlZWJlOWFhN2YxN2YwYzM0NTI3ZWU5NWM0ODdmYTQ4YWQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L2004-L2037", + "version": "repo-lines-v1:sha256:940f4f6befc1131d7e75249e7b77d093f0043727ec8dad72724605c33f9e9d89:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxY2ZjMmJmMGMwNmFhZmMyOTNjNWE3MTkzYzc2MjFhMmQwYjIyOWZjNGE1MWZjZmRlOGUwYmRiYzUwNjUyMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0NDc5Y2NlZDY2YWZmYzNjMDk5ODhiNTAzODY1NmNhNTJhOWY5YTQ0NTJjMWE0ZjZlZTQ1ZDVlYzViNDU3YTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU4ZTE2NjY1MDcwYTE2MzkwYzYyZjliN2NkNWE2ZWQ5ZmI2ODZhODE2ZGNkNjAwMjBlNTNkMzhmNjAxYzUxYjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEzYTAxMjkzY2I3Njk4NDI1NTk0MzViYzQ2ZGZjNTFhYTBhZmNlMWEzYjA0NzE0ZDNiMTdlMzA2YzdiMjQxZTAifQ" + } + ] + }, + { + "id": "claim_11686c9b9a48488e9f53260c0353341a", + "statement": "Middleware node hooks (before_agent, before_model, after_model, after_agent) return optional dicts containing state updates. These updates are merged into the state. Node hooks can set jump_to to override routing, if the hook declares can_jump_to via @before_model(can_jump_to=['end']) decorator.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L2040-L2086", + "version": "repo-lines-v1:sha256:d538d7c87169bc985bc0143aaee3b21bad50e03c28601e67bdcdba25f3a159ac:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjczNGQ2ODg5YzM4MzdjMDYyYjk3MjNlNTBiMWViYThhMzU5OWY1MjY0ZjUzMTc0MTZlMjU2Njg5Yzg1MTU1MTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY5ZWQxZWIxMTljNTI5ODU5YTdiZWJkMmUyZThmNzI0MTlmMDAzZTI2NzRiN2I3NjIxOGViZTJiMzYxYTAzNjEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZlNDk4MTU2YTQ1ZjQ2NmNhODQ2NTMwMDVjMjMxZDFhODAzOTE2MTMxMTUyZDQ1NTBiZWQyOGIwYjRlYTQ0Y2UifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L431-L540", + "version": "repo-lines-v1:sha256:15bdc34c0327effcdf8503d98ba4c621e2ac0ee863fbf91a421732ff7e6cb3e2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI1MWI2OTVmNDZkZjYzNWMwOTc2NTg0OTRjMzQ5MDNjOGI0NjQ5OGIwZTNhYzIwMzY5ODNiYmQ3NzFkM2QwZjhhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJmNGUwNTMyMzNiZGRhYzg1MDNiNGNmNGE4OGExMDkwMzliN2UwMmE0ZDY4NGY2ZjQwOTBmODdhYmIyNWQ3NjhjIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5M2U2N2M2ZWMwZTYwZGNlYmFkMzYyYmM3MmNlOGZiNGI3ZTJkMzM1Yjg4ZTk1NjkwMTI4MjRkOTUwMmRmOGQ0IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIzZGZlNDhhMDk2YTc2MTYzNWE0NWU1ZDRkM2Q4YjMxZTE4ZTZiMDUzMTM1ZGUyZjE2NTZmOTg4Yzk4MmZiMTkyIn0" + } + ] + }, + { + "id": "claim_72ba238188fd4fa69335bfdd7ea1e022", + "statement": "wrap_model_call handlers can return ExtendedModelResponse with optional Command to inject additional state updates (e.g., synthetic ToolMessages) alongside the model response. Commands are accumulated inner-first across middleware layers and applied via the graph's reducers.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L213-L260", + "version": "repo-lines-v1:sha256:dd556027d5be455837c96fee074d9d469dc9ff5f16187ca7f30054aedcf185d2:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM3YzgzN2QxZjhjOGZmMTQ2ZGFkODY5ZTlhZDhkMGNjYzY2YTA0Y2JhNjVkNzBhYTM5ZDUxMDAwNTQzMzFiNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYyNmQ2OGQ5YjUyMjFlYjU5NGVmOGJjN2JmZTNhYzVhNjM2ZjA2MjlmNmU1ZGNmNmE5NWEyMGZmMThhZDdmMzAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MjIzMTA2ZmQ4NzQyYjcwZWEyMzNmOWRjMzU5ZjRiMDJiYmIwNjliNzI4M2VlZjc2MjQ5OTUxNmJkN2I5YzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRiN2UyMTk1NWE5MGNjNjVhNmY1YmQxNTE2ODEyYThmYTQzYmNmZDhiZWJiMzMwNDJiMzg4NDZjMmUzNzYwNzQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L68-L81", + "version": "repo-lines-v1:sha256:6864d6081648a4266186d7bfa2a48ebc28536468c489f27a9bad34992e8d99a8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk3NjlkZjExY2MyYWRmMzc5MzdjZGEzYTZhM2ZkZGY3MGY2ZWVmZWI1N2I0ZjVjMzMzNDEyNDA2OTI0Zjg3MzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZjMWYwMmZkN2VjMWQ0NjJjMGNiODE4Yjk0OGZmZTRmNzE1YjJjMmM0MzI2MzBjMjMyZGZlMzRkNDI3NDNmYTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg5N2YxZTk4NWMxZjZkMGZhOGM2NDhjMzNiMjAxMzVkN2YwOTcwYjBkM2JjOTNmNWU4Y2YzMDFlM2NmZDQ3NTQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L290-L312", + "version": "repo-lines-v1:sha256:adb6dbf91250c67b61480c9fa88146be0d316818b7c66c56cc6626e40b1e2bfe:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjMGNjZTk5ZjhkYjAzYWNjNTY1OGE0OWUyZDVjMjEwY2Q3MThlYzIzNGU1ZWYyMTc0MWEyYThhM2I2MDAwOTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyMTE2Mjk0ZDU5M2IzNjY3MjYwZGVmNDViNTQ2ZDM2ZjkyMGQ3Y2Y5ODU3YWRjODE5NTQyZjBkZDJlMWZlZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU2ZDZiMWJhZDkxMjQzZDNhMTliOTMwYTY1ZTdmOGRlMDExZTAxZWJiYzA2YTczMjc0Y2Y2NWIyNmI1NDIxNGEifQ" + } + ] + }, + { + "id": "claim_4a3da71e41f94163825c83ce79fb8b84", + "statement": "The factory ships with middleware for: retry/errors (ModelRetryMiddleware, ToolRetryMiddleware, ToolErrorMiddleware), fallback (ModelFallbackMiddleware), tool filtering (LLMToolSelectorMiddleware, ToolCallLimitMiddleware, ProviderToolSearchMiddleware), execution (ShellToolMiddleware), search (FilesystemFileSearchMiddleware), interaction (HumanInTheLoopMiddleware, SummarizationMiddleware), and protection (PIIMiddleware, ContextEditingMiddleware).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/__init__.py#L6-L31", + "version": "repo-lines-v1:sha256:2ff902554f85be7b4e73f9112238fa0cbeafd02cfa79676037076f26a40963f1:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFjMzEyYjM1N2NiZGVhMjdiYjI5ZGVkZjcxNWEyNTg1NjEyODFlMzNjZGU2YWYxN2NiMTE1MGUxNTdhZmZmNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM1YjQ2MzA3ZWQ5ZDQyYjQ0MmYxOTY2YzU0ODhmZTEwOTJiZDkwMTEyZTNkYTNhZWE1MGQzOGY0NTIwNjllM2EiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhiMTAxZmNlZWM4YzM5YmZlZjgxYWVlZDc3NWZjNGY3MzYzYWRjZjUzODdmMGU5NjIwN2QyMTVhZWY3Mzk2ZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE3YWQ0NzBkMjRmNjkwYzJmZDEwZjdiMmYxM2Y0NDMxNmVjMmVhYjExYWRiOTg1MjExNGRiYzZlYzE5YzA3OTIifQ" + } + ] + }, + { + "id": "claim_1ba30ad3c66d4a59ad406975c649f396", + "statement": "Middleware can declare a trace_policy (TracePolicy object) to customize what their hooks record in LangSmith spans. The factory resolves policies at call time via _node_trace_policy, allowing configure_trace_policy to set process-wide defaults that apply even to already-built agents.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L158-L171", + "version": "repo-lines-v1:sha256:24011886b7814edd7d836fac512917a9b1d20d1cd86b2069492a72fa2fc2982d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkN2ZlNTgxZjhiYTc5YjU3MmUyYmIxZjZiYjk1MTZkYzc0ZDAxM2NhYTUxOTI3NTk1ODRhMDM5MWRiNTExMDciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQxYzlkYTk0MjI3MmRlYjljNDk0YzQ0MTA3ZTVhMDUxNWVmMzZjMTU0OGU2M2QwNjZlYTliMjZhM2FiZDc2MWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzZTk3NTc4NTQ1YjRhYWUwYjNkNjQwZTEwMDMxMzRhMTZjNmM3ZWQ1ZDdjZGM2ZGE3NzNiZjFmNmY4MDk0OWYifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/_trace_policy.py#L22-L61", + "version": "repo-lines-v1:sha256:3f43d0069b7f75e45f45b2716bf3e980ce15c4dcae233f17eadcc1b4bc9f9b7f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwZTc1NTRkODUzNDAzOGQ5NGY5YTdiYzc3NWU2YTk4ZWU4YjM5MjU0MzI1OWMyN2NkZGI2NWM0ZGJhMTk5YjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM1ZTZlNmI2MjBlMmU2MTE4M2ZhNDE0NWExMjMwNjlmODJmNzdiN2JlNjJhNzNkMjdmMTlmYTc3NTIwNWFkZmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_cc53e8a3e0b6424a8ddc10acacd0c076", + "statement": "If middleware adds tools to request.tools in wrap_model_call that aren't in the client-side ToolNode, the factory raises ValueError with DYNAMIC_TOOL_ERROR_TEMPLATE. Middleware must either register tools upfront or implement wrap_tool_call to execute dynamic tools.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L119-L143", + "version": "repo-lines-v1:sha256:5b8fa1d0424707c949a7343ed3ed758ebbddbd88d77d15ea61974764f8218bfc:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI0OTEzMDBmZGU4NjE4N2YxN2FmMzI3OWJhZDIzZDJkMmZiYjYzNTE1NzdhM2Y5MTI4NTIyYWVjNDY3MmU1YjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJhMDg1MjZjMDc4ZjBiYWU0NTMwMDNlMzYwMGY3Yzg3NTAwNTA2ZjE4MDk1OTE3NTkzNjI0ZTE4YjMwYzFiMWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg4MzA5NWJkNWNlY2U4ZmE2NTNiODE1OGM4MDUwNzE4OGFmNWExYWRjNzA4YTQzNTI5MzFjYTcxMzUyM2IwYTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE1MjU1YzcyNTUxYWI4MjdiMTY5ZTFkNTA0ZjM2NjM1MDBlYzI3ZmVhMTQzNTdkNWQzNTQ4OTk2ZTEyYzhiZTkifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1317-L1345", + "version": "repo-lines-v1:sha256:e80806d39f5a74f995e4adf40db1a3025808f1e61938b321eefd50259725bdc6:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJlZGJhZGE3OThiODJjOTdkODk3OTcyNDVjMzM4MTA2Njg0MTIzOWUzOTEyMDgwYmU2MGM3YzI2NWFjNTNlOTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1ZTZiMzJjODVmYjBmN2VhMTA1NTFjYTA2Y2VjOTI4MjlkYjE5N2M2NGZhM2U4ZGYyZjc4MGNmODczMTU4YjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE0NDliNjE4YWQwYjI2MDQzMTk1MzVjNDA1ZTgwMjEyYTQyZGI4MGFjYzhiZWFlNzRhN2QzMjM3ODc3ZmUwZDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI2OGE4NzE3MzViZjA1MGQ3MjMyYWI4MjIyYTc5MTIxM2MwNmEyZjFhOTNlNGY0MGUxYWMxMGVkMDVhYjY5MmMifQ" + } + ] + }, + { + "id": "claim_3d278ebf04ae462ea04a65d4f72ef7bd", + "statement": "The factory constructs a StateGraph with resolved state/input/output schemas, adds nodes for model, tools (optional), and middleware hooks, defines conditional edges based on model output and tool results, then compiles with checkpointer, store, interrupts, and transformers. It returns CompiledStateGraph ready for streaming/invoking.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1184-L1192", + "version": "repo-lines-v1:sha256:e1e5b640f933c9eed12b1dc48675089e352d8c003b5d2e7d70c0863dc5941b70:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzhkYTFjYzZhMjhlM2U1YjY3NTFhMWIzODNhY2ZiNTRjZjk5OGNmMDU0YmRiNWNhYWZiZmM0MWU4ZWU5ZTBiOSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzBjMWYxODhmMTQyZWQ3YTgyNDY4MGIyMDcxNzJjNTUxOWJjNjE0YTQ4ZmRhN2NmMTY3ZWRiOGRkZjc5OTU2YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiN2Y2ODMzMzdiNGMzZjU0ZWJkYWJlM2IyNjYyZTk1YzU5NDQ2YmFmODg2NjZiMjc5OTk2ODUyYzFjYTc1YTVhZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNjcxZTY1ZGNiOTY1MDA2YWI1ZjliZDk5MmY5NmY1Mzc5MzUyODNkOWEzZTBmODkyMjIzZTZjZDVhZGRhNGFjNiJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1829-L1857", + "version": "repo-lines-v1:sha256:2d79bb82fd90dd87b4fc2d7da74c23597877e8ad6f5c4a92edaf028bb97dc279:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViYzI2MWNjMzliZmRmZDMwODQ4OTY5YjFhN2IzYTZmYWQ5OGY5MjMyMDM2OWIwYjk5YjRhOWEzYTY4OWYyMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY3ODdmOWY0M2FkM2JlZGJhZWIyN2VhYjljOTAwNzg2MmFlOTNjNTEwZGM1ZTg2YTZkZDMzMWY4MDNiMzAwYzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk1MDVkZWQxZTkxMTRhNzk3YWJhZWExMTYwMTlhN2FmNmJlMDU1ZTE0YzMwODg4ODUzODEwMDI4ZWJhNzYzNGYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxZjIxZWZmMTg3ZDdlZTVjODVmN2JmODJiOGViY2E1MGM5MjEzNWI1ODA3YWI1ZWNhMjljNTM4ZDliNmFiMjIifQ" + } + ] + }, + { + "id": "claim_9668397eb706496d80edbc2cb42f07b9", + "statement": "The factory creates a RunnableCallable wrapping both sync (model_node) and async (amodel_node) implementations for the model. At runtime, the appropriate variant is invoked based on context. Middleware can implement only sync, only async, or both; mismatches raise NotImplementedError.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1441-L1540", + "version": "repo-lines-v1:sha256:532e4c0266711b50e0c35f598eeed855abac267da25c4583a95bd685d9ec2b5f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyM2Y5MDJjMTQyODdhYWM5MGI0YzRkMDgyYzhiNDk4MjIwNzA0NzZjMmM1NDI5YzFkNWZkNTFkNTcwM2FhNjdlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1OTA5YTUzYTYxOGQwMjhlMWQzMDAwYzBhN2FiY2VlY2JlNWZiMzdmNGU1MTFlZmU0NjY0M2ZhZDZlNjY4NzJmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIyMGE3OTExMzk4MDg1ZmMxOTc0MjY3MGM1Mjg4NmE2NWY3Y2JmMzQwYTAyM2VjZDM5OTJjNTM0MGE2ZDA4OGZjIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIyNDAxMjM1YTMwZWU1MDI2MmY2ZDdmMWJjZWVjMmI4M2RhODA3MTczMjJjM2IxZTM1ZDFkMmZiYjM3Zjg3M2EyIn0" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1543-L1543", + "version": "repo-lines-v1:sha256:707193ba595307f4fc96a69e6ecc2079fe02c897e4ec8e7524bd3c176e42aab6:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzA3MTkzYmE1OTUzMDdmNGZjOTZhNjllNmVjYzIwNzlmZTAyYzg5N2U0ZWM4ZTc1MjRiZDNjMTc2ZTQyYWFiNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNzA3MTkzYmE1OTUzMDdmNGZjOTZhNjllNmVjYzIwNzlmZTAyYzg5N2U0ZWM4ZTc1MjRiZDNjMTc2ZTQyYWFiNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNGY0NmQ4NTdiNGQ0YzU0YjdlMjc5ZDFhYjJlMDBlNzZkNWNlZjdmNWU2ZGY1NDVjNmU1ODY3N2RhNWMxNmI3OCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMTkyNmY0ZTljZmY4MjI2N2ZkODcxMTM3YTc4ODcxNjc1NWZhODllODVhNGRkYWNlMWJlZTRmNjQ0NGFmYmNiZCJ9" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/architecture.json b/openwiki/.claims/architecture.json new file mode 100644 index 0000000000..a6fc0669ce --- /dev/null +++ b/openwiki/.claims/architecture.json @@ -0,0 +1,154 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:4d075dd399f2c74cd6a1d03563dd5d1695887843ca5c5b920422838e41870e43", + "claims": [ + { + "id": "claim_d872799ce6874d76b63667fe912b7616", + "statement": "LangChain is organized as a three-layer architecture: langchain-core (stable abstractions), langchain (orchestration and agents), and partners (provider integrations).", + "evidence": [ + { + "resource": "repo://libs/README.md", + "version": "repo-file-v1:sha256:9fa04aa8e3984518023a2cf3aa48b0bb893c0d91fb3dae26b606f3c0b7099e73" + }, + { + "resource": "repo://libs/core/README.md", + "version": "repo-file-v1:sha256:3c67a493620030266af259b872e6f179bdbdcaaae1bb2469c0f7b8b00a3cd8fe" + }, + { + "resource": "repo://libs/langchain_v1/README.md", + "version": "repo-file-v1:sha256:21aa3f971ec38e4424df7461974da00798a679f2171e0d898dd6d4e30eff122a" + }, + { + "resource": "repo://libs/partners/README.md", + "version": "repo-file-v1:sha256:104a04a55977ae78550d262f195ad3b51ddbbf9f321318e17c39376aa2c683bb" + } + ] + }, + { + "id": "claim_bf4afec1395b4acbbf413fe2928daab6", + "statement": "langchain-core (v1.6.1) provides the stable base abstractions including Runnable protocol, BaseChatModel, message types, tools, prompts, and callbacks.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1-L50", + "version": "repo-lines-v1:sha256:ec4a224a2f0eaacffa422fe7c6e3ce9e3d1046a9ca9e78f1023c2e32ab8e6077:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE1ZDFjY2U5NjgzYzRiOGE0NjJhN2Q0NDBjYjk2ZGE3NDk3YWQ2NDYwNTE0ZjNkYWI0NDlmZjgzMzllYjY5ZjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlkMWY5ZjBhODYyNjliNDk3Mjc1ZTgxYzJiMWNiMmVmYzIxNjdlNjFjNjFkZWExOTk1MTE4ZTdiODkxZTI0NTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImFlYWJlNjk0Nzc1ZmI3OTg1MjcwYjNmMTRjNjNiOWZhZTk0MDBmNTczY2E0N2NhZWE3OGMwZTVhYmIxNjg3NDUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L133-L150", + "version": "repo-lines-v1:sha256:c8bf75fa83bb019f5e36f97570d3008efc38453777efb44cf677783d57340d21:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQwNjY0MDE5YzkwN2RmODUwZWE5ZTA1OGYxMmIxOTIzNzRiZWIzZmNjYWIwNjE5MDMwYzY4ZTY1NDhhZDgxNDkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVmMjc0Yjc2ZDllMTBkNzNmMjA1NDc5ZDlmYzBhMTU3YmUyMzM3YjMwYWE3N2RjZWMwNmViY2IyNjY1NmUxOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcxODA4NTVkNTMwZmRiNTgzMjI0MjNhNmZkZGRlOWEzYjJhNGEwOTYyNWE1ZGIzNzQzZTg5N2MwNTJiYmJkZDMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM3YmE3ZGEzOGU0NjFlM2FkZDk2MjFlYWNkYzMzZDU2NzExMTRiMzY5YWVlYzJhYTE4MjkzNTljNTRkOWYxZTQifQ" + }, + { + "resource": "repo://libs/core/pyproject.toml#L24-L24", + "version": "repo-lines-v1:sha256:238e7ddf7f3fadccf53502477ac331715e5fc6a3aada4c63ceefaa35ec509859:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMjM4ZTdkZGY3ZjNmYWRjY2Y1MzUwMjQ3N2FjMzMxNzE1ZTVmYzZhM2FhZGE0YzYzY2VlZmFhMzVlYzUwOTg1OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMjM4ZTdkZGY3ZjNmYWRjY2Y1MzUwMjQ3N2FjMzMxNzE1ZTVmYzZhM2FhZGE0YzYzY2VlZmFhMzVlYzUwOTg1OSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmRmYWFiMzNiMmUzYTUwMTM3NmZlODIxNDNkNzBmNmVkYjMyNWI4YWMzYWI5MDUwYTg0OTgwN2ZmM2FhZGI3NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGEyM2RmNzFkODljMGE5NmUzNWFhZjk1ZTdjZTllYTczYTRiN2FlZjQyNDZjN2Q3YzAzZjhlM2EwMTJiZjliNSJ9" + } + ] + }, + { + "id": "claim_54ce93defa3a454f9a4a03bc2d6ef614", + "statement": "langchain (v1.4.0) depends on langchain-core and LangGraph, and provides the Agent Factory, agent middleware system, and init_chat_model for high-level orchestration.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1-L50", + "version": "repo-lines-v1:sha256:37c541026ea7a52554d7ef187d5ebb63de7262e3fbd5ab31e15895e63f83ee0b:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiODk5ZDMyOGM3OWUzYTI0NzBmNDgxMDRjMGNhMjgyYmMyYTU2ODZlMmU3ZDhmOTdmOGEzMzFmOTQ1MzAxOGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZjMDExN2RlMmQ0N2YyNmMzOWIwNTkzNDEwNzYzYmI4NTBmNjMzMzUyOGQ2MGE3YWFmNDZmN2NmM2RkOWEzMTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgyNGRlMmE2YTc3N2ZiNWRjOTk4MmUwNDBlYzY3M2VmNWEwOGVhZmRkMTEyM2JiNWQwMWZiODk4MTZmNzI0MzUifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L40-L100", + "version": "repo-lines-v1:sha256:0df6b02c264382e864de70c5639df298325600f5ae5d73c025c60952ed7ac692:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRhZThiZWVkZDRkNzFkMTk1MDM2NTk3YjQxNTY0OTFhYjA3MTYyNGQzNWFiYWJlZDBiYjc5YzIyZWZkNWU0ZGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVhNWM3YzdlOTc3NDFmYzU0NWViODQ4YzY1MGEwZWE0NTI1Y2VhYWU0ZTgxOWJkNjQ3NzgzZjA5NzlmYzJhMmMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZmN2Q5MTY0Mzc2YzZkMThkZDZkMTNiN2U2MTZlNzdjMTc2MDBjZTQ4YWVlNTA0MGJlZTJjNmRiOWUxMmM1NzUifQ" + }, + { + "resource": "repo://libs/langchain_v1/pyproject.toml#L24-L30", + "version": "repo-lines-v1:sha256:f1e9208403e3c9fd13366581d5ade2b9c624606c9a35e4bee2b4f08fbea1eb4a:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDI0NzFjMzU4MDRjYjhkMDM3M2M4ODY1MTEyNWE2ODdjYzRmZDUzNWE5MzZkMjA0Y2QyZDFkZmIxZjM0NDgyZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjk0MTZlMDlmNzhiYTMxNmMwMzI1NjgyMTNiODU1ZWU3NTZlNDE1NjVkOTE4ZjA5NTE5YzgxZmQ2MjBmNDA2MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmRmYWFiMzNiMmUzYTUwMTM3NmZlODIxNDNkNzBmNmVkYjMyNWI4YWMzYWI5MDUwYTg0OTgwN2ZmM2FhZGI3NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDQyZWY1NmY0YTJjOWRmNjRkZDllM2VmMDBjZmZhYjIxNzM3ZjA1MWFjNTZjNzNkMGM2YTA3NDk0NDI2OGVhZSJ9" + } + ] + }, + { + "id": "claim_0495f8fc5fa4400cb6f6a1a655997d39", + "statement": "Partner packages (langchain-openai, langchain-anthropic, etc.) implement core abstractions (BaseChatModel, embeddings, tools) and depend only on langchain-core, enabling independent release cycles.", + "evidence": [ + { + "resource": "repo://libs/partners/README.md", + "version": "repo-file-v1:sha256:104a04a55977ae78550d262f195ad3b51ddbbf9f321318e17c39376aa2c683bb" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/__init__.py", + "version": "repo-file-v1:sha256:d08e06dea1f954a477fa8951e2a8387a10396d07b92a3a60fdced307c136a84f" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L1-L50", + "version": "repo-lines-v1:sha256:b972f9928545904dc248d138c943fc37ce04364c9f3ed8a4461915e774ca44b6:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwMWZkYTIxNjI4MGFjNjg0ZmI1ZGQ4NjIxYmE5NTJiMjU3YzhjZTVlMDExM2U2N2JjMTJkZDBlZGU1ZTg2MGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyODFhMDEyZDkwNWU4YzQwNTdiNzJmNGY2NWQ5ODBjZWUyNTAxMmU2MTgzM2E2MDM1N2JiZjk2ODY5ODk1ZDYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE2NDcxNTMyNmUwZDYxNDlkYWE3YTYzZDFkMDE3MmVlMWM1Yjk1NzlkZTliOTQ0NDBjZGM4MjFiNzVhYmNjMWEifQ" + } + ] + }, + { + "id": "claim_76087920b42a4f648602e9ba38bc7257", + "statement": "init_chat_model() dynamically loads and instantiates chat models by provider name and model identifier (e.g., 'openai:gpt-4o'), implementing lazy provider discovery and on-demand import.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L40-L100", + "version": "repo-lines-v1:sha256:0df6b02c264382e864de70c5639df298325600f5ae5d73c025c60952ed7ac692:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRhZThiZWVkZDRkNzFkMTk1MDM2NTk3YjQxNTY0OTFhYjA3MTYyNGQzNWFiYWJlZDBiYjc5YzIyZWZkNWU0ZGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVhNWM3YzdlOTc3NDFmYzU0NWViODQ4YzY1MGEwZWE0NTI1Y2VhYWU0ZTgxOWJkNjQ3NzgzZjA5NzlmYzJhMmMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZmN2Q5MTY0Mzc2YzZkMThkZDZkMTNiN2U2MTZlNzdjMTc2MDBjZTQ4YWVlNTA0MGJlZTJjNmRiOWUxMmM1NzUifQ" + } + ] + }, + { + "id": "claim_511c0c1f76e34bac9a84a80985df132e", + "statement": "create_agent() constructs a compiled LangGraph state machine that orchestrates the agentic loop with model invocation, tool binding, structured output parsing, and middleware composition.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1-L50", + "version": "repo-lines-v1:sha256:37c541026ea7a52554d7ef187d5ebb63de7262e3fbd5ab31e15895e63f83ee0b:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiODk5ZDMyOGM3OWUzYTI0NzBmNDgxMDRjMGNhMjgyYmMyYTU2ODZlMmU3ZDhmOTdmOGEzMzFmOTQ1MzAxOGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZjMDExN2RlMmQ0N2YyNmMzOWIwNTkzNDEwNzYzYmI4NTBmNjMzMzUyOGQ2MGE3YWFmNDZmN2NmM2RkOWEzMTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgyNGRlMmE2YTc3N2ZiNWRjOTk4MmUwNDBlYzY3M2VmNWEwOGVhZmRkMTEyM2JiNWQwMWZiODk4MTZmNzI0MzUifQ" + } + ] + }, + { + "id": "claim_b8efad0f42a84e1bb166defa5b475278", + "statement": "Agent middleware composes vertically via hooks (wrap_model_call, wrap_tool_call, before_agent, after_agent) enabling orthogonal cross-cutting concerns like logging, validation, and rate-limiting.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1-L50", + "version": "repo-lines-v1:sha256:37c541026ea7a52554d7ef187d5ebb63de7262e3fbd5ab31e15895e63f83ee0b:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiODk5ZDMyOGM3OWUzYTI0NzBmNDgxMDRjMGNhMjgyYmMyYTU2ODZlMmU3ZDhmOTdmOGEzMzFmOTQ1MzAxOGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZjMDExN2RlMmQ0N2YyNmMzOWIwNTkzNDEwNzYzYmI4NTBmNjMzMzUyOGQ2MGE3YWFmNDZmN2NmM2RkOWEzMTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgyNGRlMmE2YTc3N2ZiNWRjOTk4MmUwNDBlYzY3M2VmNWEwOGVhZmRkMTEyM2JiNWQwMWZiODk4MTZmNzI0MzUifQ" + } + ] + }, + { + "id": "claim_2f4f0387590e4a5a9a12f07d3bf0e6c5", + "statement": "AgentState is a TypedDict with messages (reducer-based accumulation), jump_to (ephemeral middleware control), and structured_response (parsed output), extensible via middleware state_schema declarations.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py", + "version": "repo-file-v1:sha256:56688206dce0334e7ca82125d7b809c23427890c234dfd69123dad038a387f31" + } + ] + }, + { + "id": "claim_04b9d894ec7f407399bc3e2adfe68ef6", + "statement": "langchain-classic (v1.0.8) is legacy and depends on langchain-core; new projects should use langchain instead.", + "evidence": [ + { + "resource": "repo://libs/langchain/README.md#L1-L30", + "version": "repo-lines-v1:sha256:44cb1bfcadf8fed46c15e030140879628d970b883ceaa268a8ed2a0eb91f0848:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFkNDNjNTE1NzI0MDZkMTFiMWMxNjVlMzcyYTM3MjdmMjE3Y2ZlMjg5OWZiZTJiM2QyNzRiZjY5NDRmM2NhYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE5MDZjNjU1OTZhOGYwMjMzZDZlNzZkZmQ2ZDc0MWY1ZGE4ZjJiNDk0MTg0NmMwZDk0ZDlmMzFmNTk3ZDk5ODkifQ" + }, + { + "resource": "repo://libs/langchain/pyproject.toml#L1-L30", + "version": "repo-lines-v1:sha256:76462a1d57f7056ae2847a3103a265411c9ddc9f5c4960f7a1aafb8a1e60866b:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA0NTY5OTQ2YWM5YmJlN2NiMmNkZWMzZjhiZWNmMzQzNzUwYzU4YjliMzIxZWMyMTk1YmZiYWFiMmFhOGM0YmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2ZDEwODE0MWExNDcyNzA1OGEzMDJmMDhlYzA1YzM3MzVkNGYzNjA4NjJjOGIzZmIyZWQ5YjdmMGFhMGVlZDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZiZTY5MjM0ZGI0YjFmMmQ1NTgyMDZiMjBiOTQ2MGRjMjE5N2IyNmYyOWE0ZDVjNDlmMTQ1YzcwN2VlMGMxYWQifQ" + } + ] + }, + { + "id": "claim_34b02e543c5a4e54a935719f329e47e9", + "statement": "langchain-core maintains stable versioning with advance notice of breaking changes; langchain and partner packages are released independently.", + "evidence": [ + { + "resource": "repo://libs/core/README.md#L39-L42", + "version": "repo-lines-v1:sha256:2270c5c730eb95d2262ac4fdfd81f01dc541ca375c255d4fa2ac1b445c7ddce9:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjRjNThlMGJjY2M5YjcwMjI1MDZlOGQ5OTE1OGIwMzY0ZGE4Y2M1MTcwYTE1MmNiYTAwZDcxNTM0MjQ5OGE3ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiY2IxNjc4OWJmMDRmNzQwZmQ0NGE4ZDc1ODViN2E1ZjM5NmY0MGM1Mzk1NTlmMmM4MDEyYjU3MDc3NTdiMDU2ZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMGEyM2RkZTBjNjFmY2NmZTA5ZTE3OTFiMzUzYjAzODI1ODU3MjNiNDlkOTliNmEyMTUyZWNmMzZmY2YzMDc2MCJ9" + }, + { + "resource": "repo://libs/langchain_v1/README.md#L31-L33", + "version": "repo-lines-v1:sha256:8dfabf0afb2a34a0d10ef2d5bd39d19322131ad902c8e6b1f5339fbd5cc11e1a:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjRjNThlMGJjY2M5YjcwMjI1MDZlOGQ5OTE1OGIwMzY0ZGE4Y2M1MTcwYTE1MmNiYTAwZDcxNTM0MjQ5OGE3ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTExMjY3N2NlN2U0YjczZGU3YzlmYjI5MWU4ODkyODNiYzk3NzM4Y2VhYjEwMDI5YTg3N2FhYWM2ZWI4ZTJmMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNWM1OWViZTEyOTE2YzBlYTg1MjNlZGMwNWNjM2YwYjJkMDhlMWQwZGVjMzdjYWYxNWUzZTVjMTQ3MTY0MjYwMSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZWU2OTgzY2FkY2E1MTk3MWNmMDJkNmRjOTZhMGRiNjlmNzJlMDNiOTkwZTEzMGQ5ZTBlNDA0YTA3NTFhZWNlOCJ9" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/callbacks.json b/openwiki/.claims/callbacks.json new file mode 100644 index 0000000000..e13c248cfd --- /dev/null +++ b/openwiki/.claims/callbacks.json @@ -0,0 +1,198 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:a3d31bdc7065f8a6bb1a5dc81e82f5b216c238dffdf3e1c523d8a0ed7cf0558d", + "claims": [ + { + "id": "claim_1870ae33ffdb41fa87eaee4125620240", + "statement": "BaseCallbackHandler is the base class for all callback implementations, inheriting from mixins that define event methods for LLM, chain, tool, retriever, agent, and run operations.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/base.py#L496-L546", + "version": "repo-lines-v1:sha256:d723f5a7310cbc3ff40d2189bbb0d1ae8f86c18ecf557e1e4c233eb69ec3b81f:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlkYjFmOWJkODZiNzhlYWM3NmI4ZWVmMTk1NWNlNDQ2OGI0NDU4ZWJkNTU0YzAxMjU2M2Q0MWE3YTY2N2JkMjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM4MzA2YjE3YTFjM2I3MjA4Mzc2OWI4OGM1YjAwN2I5NmEzNWEwNDJmNzllYWI1ODUzMDY3Y2JlZGQ0MWFjOTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMwMDFmM2NjZGQxNjYxNDg3ZjBiZjYxOGQ1ZGU3ZjcwMmI0NTg4MGUxNWY5MzJiOTRmMjRhNjJlNzAwMzdjZDQifQ" + } + ] + }, + { + "id": "claim_e6bcc399e1764926956cf083b9adea2f", + "statement": "BaseCallbackManager maintains handlers, inheritable_handlers, tags, metadata, and parent_run_id to manage callback lifecycle and enable run hierarchy.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/base.py#L1004-L1227", + "version": "repo-lines-v1:sha256:fab300200f79cf4fce639ecd69b4cf2f3c7495b4e45483242a67b1ec584fc110:eyJzZWxlY3RlZExpbmVDb3VudCI6MjI0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJhN2EyY2E2ZTQ5OGM3MmE1ZWQyZDgyMTdiOGVmMDEzZTVhYzk0ZmY3ZjRiM2Y0MWRjNWRjNTIyNmMyODZkMzkzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzODMwNmIxN2ExYzNiNzIwODM3NjliODhjNWIwMDdiOTZhMzVhMDQyZjc5ZWFiNTg1MzA2N2NiZWRkNDFhYzkzIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MiwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmYTgzOGRlYjQ3ZGFmYWVkMTdjODg5NWI0ZGNjMDk1NTFjY2Y5ODQ4MGE0OTk2ZjNhNTVkODM4MGViNDQ5YmRkIn0" + } + ] + }, + { + "id": "claim_b4a16de3887c4279a15b82fd84b917da", + "statement": "CallbackManager (sync) and AsyncCallbackManager (async) dispatch events to registered handlers and return specialized run managers (CallbackManagerForLLMRun, CallbackManagerForChainRun, etc.) bound to specific operations.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L1377-L1726", + "version": "repo-lines-v1:sha256:850cfed4d8ba543380b609a75924081f90cd44992b7d4386d8b97b4c79b21d33:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmODNkY2VkNmY2MWI1OGMxODZmMTBmYWE2NDNlZjQyM2E0NDgxZDU4MDFmMjg2NWZkZTA5ZmMxZDhkMGExYjIwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJiM2RmMmFjZjA2ZThjM2YwM2M3MTM1YzZlOGFjNWE5Y2E1NDMyYWJlMDZhNzRhY2EwZmRkOWE1M2EwMDhmOTg3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1ZWE0ZDc2OTA1NWU4MDMxMjliYmExNGFjNzZkNjA0NGEyOGMxNzM0YTM5YmRhNWQ1OTJhNGFmOGYwYmI2Yzk4In0" + }, + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L1859-L1859", + "version": "repo-lines-v1:sha256:50110a5275ca6b3ddcb6ecfcd67d7a59d6c0ce6dada2938843f0e41ff0beffab:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNTAxMTBhNTI3NWNhNmIzZGRjYjZlY2ZjZDY3ZDdhNTlkNmMwY2U2ZGFkYTI5Mzg4NDNmMGU0MWZmMGJlZmZhYiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTAxMTBhNTI3NWNhNmIzZGRjYjZlY2ZjZDY3ZDdhNTlkNmMwY2U2ZGFkYTI5Mzg4NDNmMGU0MWZmMGJlZmZhYiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMTY2OTUzZDRhZDQzOTkyMzM3ZjEwMGI5YmJiMTdjZTU2ZDcwNDRiY2ExYzAyMGM0ZDA4Y2FlMGUxOGJkNDI1OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMWJlOGQ2YmY2YTNhY2QwZjZkMWVlZjg3ZjA3MGI5ZDQ2YjQ5YWVlOTY5ZTgxMDZkZWQxYjk4ZWMxYWZiZTA1ZiJ9" + } + ] + }, + { + "id": "claim_c93c461011d6415e836d7a8183eb9a2b", + "statement": "handle_event (sync) and ahandle_event (async) dispatch functions invoke the corresponding event method on each registered handler, handling exception logging, ignore conditions, async/sync execution, and fallback conversion from on_chat_model_start to on_llm_start.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L285-L368", + "version": "repo-lines-v1:sha256:47e14195ad0b3a69ea2519c38d2f8261711f72db3b71bcd87d6c1bba2a08c0b2:eyJzZWxlY3RlZExpbmVDb3VudCI6ODQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJmNmVmMDE0NTNkNmZiMmY5YjliMzg1YmU1ZmE2NjBmYjMxOGU4OTQzZmE1YWE4NzRhYmViYjU2ZDQ3OGY5ODAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIzZGYyYWNmMDZlOGMzZjAzYzcxMzVjNmU4YWM1YTljYTU0MzJhYmUwNmE3NGFjYTBmZGQ5YTUzYTAwOGY5ODciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAyNmJjYzg0N2NhOGI5ZjAxYmIzMTY1MWZkYzlkYzM1YjkzODc4NGYxNTU2MDI3NWM1ZGJmYjM4ZWJlZDM3ZDYifQ" + }, + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L453-L488", + "version": "repo-lines-v1:sha256:5d0ac2b06d0fe50cfb83dfc172645cde5eb7c60b8eb795af7f8631d13af5c9d7:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiODQ2Y2ViMmM5ZjE4NGQwNGI1ODMwN2I3NjJmMDczNTkyMDYzYWE5Yjk4MmFkOTU1ZmExMGY5ZTY4OTZmOGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxMmIzNWM5YTE3NDNhODgwOWMzMjI0NzA5YmU5NGM5Y2Q3M2YzMzhhY2IwMDIzYjg1YzU1MDc2NzFlZjcxZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRiZGVhMGEzZGEzZWViNWRmOWZjZjQxNzZiOWVhMGUyMzJkYzNmYzYyZTk3ZGM0NzNlODQ2ZWZjYzU4ZjQ1ZmEifQ" + } + ] + }, + { + "id": "claim_5705e285a8c047d9bc28f252aecdb606", + "statement": "StdOutCallbackHandler prints human-readable messages to stdout for chain entry/exit, agent actions, tool observations, and text, making it suitable for interactive CLI applications.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/stdout.py", + "version": "repo-file-v1:sha256:7ddafa97f73297a0e5168a2db6e01871d08783649a0f343701cd57c75124d578" + } + ] + }, + { + "id": "claim_efbceb2355224416a369c6851893b2c5", + "statement": "FileCallbackHandler writes callback events to a file and supports both context manager (recommended) and direct instantiation patterns for resource management.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/file.py", + "version": "repo-file-v1:sha256:eb9ab3752c9f34dd6a5d4d4c146c336f21abd35128a6d2efbff0e3a52f9de41c" + } + ] + }, + { + "id": "claim_dc7f9ea5440941dd8c4278e9a46d2ffc", + "statement": "StreamingStdOutCallbackHandler streams individual LLM tokens to stdout as they are generated via on_llm_new_token for real-time output visualization.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/streaming_stdout.py", + "version": "repo-file-v1:sha256:a2a4b1629a908d76fa56a8eb7d2f0c976c21bc8666de92796eaae109e4b1fb4b" + } + ] + }, + { + "id": "claim_078559422b0a4ebd948cf6685504bdf3", + "statement": "UsageMetadataCallbackHandler aggregates token usage metadata across LLM calls keyed by model name, using thread-safe locks for concurrent access.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/usage.py", + "version": "repo-file-v1:sha256:5cc78a3ad61dce9fbcfce69b3cdf9813a459f4fd9bdf7fdaf6d4cb9ec307891c" + } + ] + }, + { + "id": "claim_42969e38018f425c896f33fc69b02806", + "statement": "LangChainTracer (from tracers.context) is automatically registered when LANGCHAIN_TRACING_V2 is enabled, capturing all run lifecycle events and building hierarchical trace trees for LangSmith.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tracers/context.py#L105-L130", + "version": "repo-lines-v1:sha256:a2e02829089efe415de6521d2c0c70ced00e85c4643a9699c977b28486c2d680:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZlNWUyMzMyNDYwZjc3NjFiYjUxNDdmNGEwYzYyYmM2YjM0YTQ4MGEyNjFhNzg0MDVhNWQ3OWUzNTAwODVkYmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3OTQzZTk2MzdiMTZkZjk4NTc2YjFjZDA1NjI2ZmJmMTE2ODAxODk5NGI3ZTI4NTBiYzRhM2E3MTQ3ZmVkNmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg3ZmJhZTY1NWUxYmM2NDk4OGVlYTQ2ZGIxNzA5MzU4ZDlmNGNiZTNhOWM3NjZkNzM4NjJlYzUwNGZhNjczOWYifQ" + } + ] + }, + { + "id": "claim_15274053b3594468973e45492688fbde", + "statement": "Callbacks are registered via RunnableConfig (with callbacks, tags, metadata, run_name, run_id fields) or CallbackManager.configure(), enabling flexible configuration of handlers and context.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/config.py#L57-L129", + "version": "repo-lines-v1:sha256:def5c5af24438aa68655c3dd7c5616745bc16d1e92d9f85b281622790264a60d:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ0NDE1ZGIxNWIwNDQyZjJiMWY1ZjNmZDNlMGVhMzIwOGVhYTQxNjRlOTJkMTU5Mjg1ZTNjOWQyZGM2OTEyZWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY0NmVhN2E5YWU3NmIzNTI0YTM4NWUzMmEwN2E1NmQwYTUyNjI1NTU0ZDAzNmI0ZjFiM2JkOTJlYzc3ODcxMDkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU0ODY1YWQ4MzExYTZlNGE5ODNmZjNhMjk0Mzk4NTIzNDNjYTkzNjhjNmExNTU1ODIzMThmMWI3NTBmMDJhOWIifQ" + } + ] + }, + { + "id": "claim_29e15c1e7af446be931344f27319c3a1", + "statement": "The callback system preserves run hierarchy using parent_run_id references, with ParentRunManager.get_child() creating child managers that inherit tags and inheritable_handlers.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L599-L619", + "version": "repo-lines-v1:sha256:9bbb2291b36c8f4221990f9b23642b522568d49117d4a148c471cb7c659161e0:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ0MzIxMDc1NWM2MGMyNTQ0MTdiNTkzYzEwODllMTc5ZjhmMTE3MDJmNzc0NTIzMDcxZjVlMjE5Y2ZiNmVlYTgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIzZGYyYWNmMDZlOGMzZjAzYzcxMzVjNmU4YWM1YTljYTU0MzJhYmUwNmE3NGFjYTBmZGQ5YTUzYTAwOGY5ODciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY1MjhmNmE2MmFiY2Y1MTNjOTFlMWJiMGNjODZiZTA1ZjI4ZTZiNDhmMWUwNjYwODg0YjA3NGFkNWRlNDBkN2MifQ" + } + ] + }, + { + "id": "claim_43458f4908784462ac48b464c016bd7d", + "statement": "The shielded decorator preserves context variables in async handlers when cancellation occurs, avoiding deadlocks between the event loop and callback execution.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L221-L254", + "version": "repo-lines-v1:sha256:c3789553697c59a07711fda8e0514f21464486fa2ccbdb49b310429e34fcad18:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2OTc4YjhmNzZjYTM3OGMxODBiZDQyYTVlYzM5MzljODY3NDU2NGVjNTlmYzEwZjMxOGYzNzUyMDc3NDgyYTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjlkMGNmMmQ1MTNmY2U0MGVjN2QyMjY1NTA3M2VlMmEyZWQ2ZjEzNmM1ZDZlZGNkNmFkZWU5ZmZjYzc0NmEyM2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIxNmFhYTU0OGU5OGE5M2EyMDI4YzA5M2U2ZTM5ZDY1MGViZDNhZjYwNWExODIzN2Y5Y2RiOWFmNTRjMTJjNDMifQ" + } + ] + }, + { + "id": "claim_7fe8d08eda3f46d3ae32b897aa4fd008", + "statement": "Context variables (tracing_v2_callback_var, run_collector_var) propagate callback configuration across sync/async boundaries and thread pool execution via copy_context().run().", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tracers/context.py", + "version": "repo-file-v1:sha256:149a9f5b662baec09d1a41555c60ed005b9d65d29d015bd1dae82a3c7be05dab" + } + ] + }, + { + "id": "claim_adaa020ca56e49cbae79e280e5e96d6b", + "statement": "The run_inline attribute on handlers controls execution: run_inline=True executes sequentially in caller context, while run_inline=False executes concurrently via thread pool or asyncio.gather().", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L1867-L1954", + "version": "repo-lines-v1:sha256:791e542664e098b75a3c696bbfb35d579d5b207fab293629e79288110f937548:eyJzZWxlY3RlZExpbmVDb3VudCI6ODgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM5MjExYmEyMTNjMjAwZWFhMGY2OTQ3Y2NlZGVjOGVmMTMzOGQyOTUxYzVlNmRmZjllNDRmYWQ4OGZlNTRjYWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg0NGM0NDBhN2Y0YWRlMGI1ZDRhOTUxOTQ3NGFiMWJlNzhhYWYzYzg2MmNhYjNjYjQyMzJiOTFmNTEzODBkZjIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3ZTkyNjlhYWMxOTQ2YjM0MTUzNjZiMjhhZTA2ODI5NTY0MmU1MzFhOGFmMjY5NmQwOWI1ODVmZmM5NzA3ZjAifQ" + } + ] + }, + { + "id": "claim_94794de66ad64909ae2591d1de986551", + "statement": "Ignore condition properties (ignore_llm, ignore_chain, ignore_agent, ignore_retriever, etc.) allow handlers to opt out of specific event types during dispatch.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/base.py#L512-L545", + "version": "repo-lines-v1:sha256:a48df3a39303b1be983f2e35656ca6fa189323cf686f30963da59e136c4f0fa4:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZDAwY2YyNzA5NjRkNDRhMTNkZmFjODc1MmIwOWZjYzQzNWIwZjE5YjMxNmFiY2Y4MWE3YWE2NGJiMTIyZDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI5N2M3MTI0N2FmM2NkMWVkZjU1NWE3ZDEwNzU5MzE1N2Y2ZGMyYTIyYWYwZGU4MmU3Nzc0YWJmMTBmOGFkZTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNmODljYTA0OWNmYjA1MWE2NWQ5ODM1ZGNlMGU0YmEyY2FkNTg2YmRkYTgzZTA0YWRjN2Y2NTIzNDQ5ZjBkMjYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc3ZDU2ODFmMzNlMGVhYzc4MmNkN2Q5YjYwMzczMDViZDk3ZWZiM2UwYjMzNWRlN2RiOWIyNjYzMTNiYTA0NjAifQ" + } + ] + }, + { + "id": "claim_481d10ef39464ff5bc6e8dbecd4ef143", + "statement": "The raise_error attribute on handlers controls exception propagation: when False (default), handler exceptions are logged and swallowed; when True, they propagate to the caller.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/base.py#L506-L507", + "version": "repo-lines-v1:sha256:3b1a3520c72ea61be1999110f2b66f397a0bcfbcf8fdd5865542cdbe412605b4:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTlkYWU1ZmY5MjRiMzU0MzY1NmRlNGYwYjhjODI3MDFlMjExODgxMzkzMGYyMTdkM2VjMWZkNjNlMjY4NzRhZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjMxOWY3MTI2NzlhZjk2MzRmZWEyNzYwNDA0MjI1NGE2MDA3MDRjYjM2ZjY5YjdmODMxZjc2YTM5NWFlNmNlNCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTNkMGI2YTA4MGMxODI0NGJmMTgyZDBjZThiYTZlYTY3OTQ4YmJkYmU2N2Q5NmNlMWJlOTk4ODMwMmMxMDg1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTZlODU0MTNkZWNlOGQ4OGMxZjc1YjA4NzgzZGNiMGI0YWU2MTdiMTBmODBjY2NlZmJiOTA5NTkxZDJhYmY3YiJ9" + } + ] + }, + { + "id": "claim_bff81d9a96744e2bb5079d3b442c6975", + "statement": "Custom events can be dispatched via manager.on_custom_event(name, data) to handlers implementing on_custom_event, enabling application-specific observability events.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L1638-L1680", + "version": "repo-lines-v1:sha256:66a5a200c78fabbb48203c9384188c9330f7ab39baf25c4e36e04faa1ac0ae59:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3MGIyZTI0ZDcwNzUxZGY3YjIzYjdkNTNiZmYxMzJlNTNhZjhhYmVhNTM4NWQzMTEzOTc0NWNkMDNmMWRjZDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFkMzIxOGNmOTk2NmM4YTNjODY2ZmNiOWJiNWRkZjM1MGY2ZTA2ODEzOTA4OWMzY2YzOTZjZTdkZTIwMmYxN2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI2MzAzODJlZTJiNTJhYzcxNWVkMGMwYWNiYjlhYmFhZDc3MzcwNmI1OTJlNjEzMTcyMTE5NDdhYzY1MTYxNjUifQ" + } + ] + }, + { + "id": "claim_058ced639933463a9ec9a4f2d9a2b39c", + "statement": "trace_as_chain_group and atrace_as_chain_group context managers group multiple separate calls as a single logical operation with unified tagging and tracing.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py#L55-L133", + "version": "repo-lines-v1:sha256:5c88290ff1791d3a8b6cc5c198f1d77c0310fa88bf696f750dfd59b60cf95ed0:eyJzZWxlY3RlZExpbmVDb3VudCI6NzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU2MWYzM2MxMzI1NjlhNzQ5NDZiMmZjZjU0NmNhM2M0NDI1ZGEwY2E1Y2Y2YjY4NmMyNjA5MGZhYTlkMTEyYjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ4ZWFhMTI4Nzg4ZGE4ODYwNjExOTAyY2NiZWI1ZGVkYzk0NGNkZmIxNWYzYjQ5ODc2YTc5ZTUyYmRhZGNlZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMwNzExZjJlM2U0MTEyODY3YTQyZDJmZGFkZjNmNWRlNWMyNDgyMjY0ODY2NTA2YTQ0ZDgyY2I3YmI1ZmFmNDUifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/chat-models.json b/openwiki/.claims/chat-models.json new file mode 100644 index 0000000000..5ec074f626 --- /dev/null +++ b/openwiki/.claims/chat-models.json @@ -0,0 +1,308 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:d6bff3b81a95d8ca6bc4bdbd9b5c49d4db94d4b506fe48361a3571a0a8f2a35f", + "claims": [ + { + "id": "claim_d2cdcf05dd4149bb9ea0a5fb0d1568d8", + "statement": "BaseChatModel is an abstract protocol class that all chat model implementations inherit from. It is a Runnable that accepts LanguageModelInput (PromptValue, string, or sequence of messages) and produces AIMessage outputs. Subclasses must implement _generate (required) and optionally _llm_type, _stream, _agenerate, and _astream methods.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L284-L332", + "version": "repo-lines-v1:sha256:cfd32376cbed1e686d0204121882ac2c99a34deac67b48da8ef025034c33a85a:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEwMDU3MWQwODUxYzEyMjE1M2EzYjAxMTUxNTUyOGY5YTQzNzBkZmVmN2EzMWRmMzU2ZThkNzljYzIwODQxNTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhMjIxODk1NWJjM2M2MzdmNGU5ZTQwNWQyY2JmNWU4Yjg4NTBkYTAyYWQxODEzM2I1NDMxNTllMDljZjQ3M2IiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU1NjVjZjg2MzBjZDY5ZGJkYjA0ODg0NzFmMzM2MWY3NDMzNzU0MjAyNDFlYTA1MjNlOTMwODFkMmZjNzFmYzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE5NDQzYWE2YmMzZTM2NGNkZjliZTJlN2JiNDIwMGFiZjExMzUyMzVkZjQ2MjEyZmIyYWMzOGEyMDVhYmNiMWEifQ" + } + ] + }, + { + "id": "claim_827f2a3f82984673bc9aedc818bcec64", + "statement": "BaseChatModel provides synchronous invoke/stream and asynchronous ainvoke/astream methods that handle message conversion, callback lifecycle (on_chat_model_start, on_llm_new_token, on_llm_end, on_llm_error), rate limiting, and routing to streaming or non-streaming code paths based on model implementation and configuration.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L474-L523", + "version": "repo-lines-v1:sha256:9530ef00f9f42bd871ffa6b861c523bf4bd6383b0525873a8ef722979812dfd3:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ0YWMyMDlkNjcyODM2YTRjM2RkMGVjZWQxMmRjMGU2YjZmNzhkMzgxZmY1MTY4YjdjMDdhODQ3NWRhMTlhMzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc4OGM3ZDI0YTAwNTU4ZTA0NDRkZDA1NWNiNTAzZjJjOTE5YjY4Y2QzNzhhM2NiNTU4Mzg4YThjMzY4ZDdmZWYifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L726-L990", + "version": "repo-lines-v1:sha256:145b830db054fb6650ec383cec82317fa44c9cd6bd173f2c6ce94ecbbf300aec:eyJzZWxlY3RlZExpbmVDb3VudCI6MjY1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0NGRhOTM1MDAxOWRjMWU2Njc0NTI2NDM4NzMxNjE5OTcyMjVlOWNlNjAxMDdhZjdiNjRmODQwZGRlY2ZkYTRmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1OTA5YTUzYTYxOGQwMjhlMWQzMDAwYzBhN2FiY2VlY2JlNWZiMzdmNGU1MTFlZmU0NjY0M2ZhZDZlNjY4NzJmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3ZTkzNzcxNDA4NjQxNGUxZDQ4NGZlZDU1ZTkzMjVlNWExNjFlY2Y0MTcxZmZlYjdkYWI0M2QwMDgyYjZjODRhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5ZGYxYTdhNTVlNzBlNDQ2YzhjY2RkYzkxZjRjOWY2ZDg5YTc4ZmVjMDNlZTQyN2U1MTA5MWVkOGRhMmJiYzM5In0" + } + ] + }, + { + "id": "claim_dc67358634a74aeba7bff0a85313ecf8", + "statement": "The _should_stream() method determines whether to route through streaming APIs based on: (1) whether _stream/_astream are implemented, (2) disable_streaming configuration, (3) explicit stream=True/False kwargs, (4) instance-level streaming attribute, or (5) attached _StreamingCallbackHandler. If streaming is unavailable or disabled, stream() falls back to invoke() and yields a single chunk.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L525-L585", + "version": "repo-lines-v1:sha256:45eff9fcf247512c468032e165dd24e8c1ffc75e9a5f52d2780e137cad20140f:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIzOWQ2MmMzNjVjOTdiZWM4ODU2ODFmMjc3NGE1YThmM2MxNWZiNzQ3ZTRiMDRlNGMxMGQwNzA4NjM5MzQwNjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdmNjljOTFlZmZjYmU3NjUzMDM5M2JmNjExMmZlYTA1YTYyNTcwMjVlMTI0ODMzZDMyNjIxNDIxNGE0MjU1MzkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJiOTA1ZjA1ZmUzMDYyZjUzZmQ2MDRhMjI1NDY0ZGVkYTQ2ODg4OWI0ZDdiMTk1ZGQ0MzAzMjZmYWFhMDFmZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjViZWVkOTEwZWVmYWMxOWJiOGJiNDdkNDVhZGE4ZWUwY2QwMDQ3OTY0OGJjMjZlYWZiNzBmYzQ1NmVmYjM1MDEifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L726-L741", + "version": "repo-lines-v1:sha256:cacbd449489fb9444e90e81dcf1ccc0e634841564f80ffa8520f3805bd9e4dd2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE5N2I0N2JiODFjZTkzY2JhNjdhZmMwODY5OGE4MGNmYTA5M2E4NjY3ZTljOWY5NjUzMzc4MThkNjU3OGQxODUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdlOTM3NzE0MDg2NDE0ZTFkNDg0ZmVkNTVlOTMyNWU1YTE2MWVjZjQxNzFmZmViN2RhYjQzZDAwODJiNmM4NGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc2N2EwNTgzZTViYzVjNTQwOWY3OWE4NTU5MWVjODcxMWJkODUwYjljNDZmYjBhZmU5OWU2ZTE5NWMyODA2MzMifQ" + } + ] + }, + { + "id": "claim_88a3bc68c8c44f8d8e90f4e8f99af29d", + "statement": "Subclasses implement streaming via _stream() (sync) or _astream() (async) methods that yield ChatGenerationChunk objects. These methods receive normalized messages, stop sequences, and a run_manager for per-token callbacks. The default _astream implementation runs _stream in an executor. If neither is overridden, the model is non-streaming.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2255-L2311", + "version": "repo-lines-v1:sha256:731ced876db905c6d0950307f17a0523039ddf745c88e7c76ca4f706c988ac19:eyJzZWxlY3RlZExpbmVDb3VudCI6NTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkzN2Q4Mzk0YzBjZjdhYWJiZTRhODg1YjA1NGE4ODI5YWQ3NGRhYmU1MGVmYzQ5NDg3NWJjZGVmNDQyMTA5YjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJjZjA5MWM0MTIxYTM5MTNhMWU4YmExMjg1YWIyNWRiOTFiZjVjNzY5OGUyYTgwOTI4NWQxM2EyMjZlMDRiODciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRlNzE1NTY0MGQ1YmM3OGYzMzcwZWQ3NDRjMTdiYzZjM2E1NDM5ZWNhYmEyMGZhOTRjZjYwYmRkNDliODQ4NmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRlNzg1MjhkOTU5YzM1YTEyZWNkZDkxMjAzZTVkZjFkNDY4YTNkYjQ0Y2YyY2FkNzc5Y2ZiY2IxYzVhZWEyYTYifQ" + } + ] + }, + { + "id": "claim_b7e494bdfca8440db5198397500c6093", + "statement": "ChatModelStream and AsyncChatModelStream are returned by stream_events(version='v3') and expose typed projections (.text, .reasoning, .tool_calls, .usage, .output) that accumulate protocol events as they arrive. Each projection can be iterated for deltas or awaited for the final value.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_model_stream.py#L1-L46", + "version": "repo-lines-v1:sha256:da766bf0c8862bfef9808624d302620cb3140e480dcd61508c3351300de0cc2c:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ1MTcxNTdmOTc5MDY1N2FmMGViZGVjZjNjM2ZmMTA4ZTQ2MGNhYWEzMjQ5MjczOGIxYThlMzU2YTI2MGE3NTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwNTUyZmUyNDUxZmNkMGJjYWQ3Y2IzMWViM2NiYmRmODRlZjliNDBiNWE4ZDJiMmZkNGE2NDFkNjk3MzdkZjAifQ" + } + ] + }, + { + "id": "claim_4241369e9483403b9c2cea053dc229da", + "statement": "_generate() is the required abstract method all BaseChatModel subclasses must implement. It receives normalized BaseMessage list, optional stop sequences, and a run_manager. It returns a ChatResult containing a list of ChatGeneration objects (each holding an AIMessage and generation metadata).", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2208-L2226", + "version": "repo-lines-v1:sha256:7dc9ec20b0a88c54a871331ce5f6dadd49655125dca20d86e156c6f2d752e509:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjNzA2NTVhMDI0OTg1MzI0MWVjMjRhMTQzZDNkMWY1ZTY2NTZiODE4YzE0Yzk0Y2E3NDFhNTU1ODkzOGNjOTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc0ODI5NTQ2NjAzM2RhYTgwODFhNDBjNjcyYWFkNzA1NDk1ODQ0NTU3NWNjOTFkZTU4OTE5MWU0ZGQ1ZGIzY2UiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImYwNTRjYTQ4MTRkOTViNjg2MDI0ZTZiM2I5YzRjOWU5MmM0NWNhZDdlMzJjMDk0Y2I5MTM4ZTQ2ODA5MmVkYjgifQ" + } + ] + }, + { + "id": "claim_8c5628590b924439b189efce4c20ac96", + "statement": "_agenerate() is an optional override for native async support. The default implementation runs _generate in an executor via run_in_executor. Subclasses override to call native async provider APIs.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2228-L2253", + "version": "repo-lines-v1:sha256:fac4b6218fce15d131280a4526267491b6aa6a769235926ebb676cacf677a1cf:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQyOTMxNGIxZDFkNWVhNjFjYWE2M2NiYzIyMTM1YjM4YzQ1MWE1ZWRlOGU2MTIzNjFkMzk1OWE1NmQwZWJmMGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjYzMjQ0MWRhZWNmMDMyZjQ0NTIwOTRjM2Q3OTI5MDMyZjc5YjcyOWFhZmNjYmU3YTA1YzFmZWYzYTUzMjc1NTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcwMGY3Y2UzNzEwYTczMmI5NjU4ZGMwNGIyYTI3M2YxNWYxM2FkOGU0Y2JkZGExMzRhYmQyZjY2ZTYzMzEwZDkifQ" + } + ] + }, + { + "id": "claim_2c69747d436f4b7e87260f3b184a7875", + "statement": "_generate_with_cache() and _agenerate_with_cache() wrap the core generation methods to transparently check and populate prompt caches via self.cache or get_llm_cache(). Cache hits replay v2 events if attached handlers support them, providing transparent caching with consistent callback behavior.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1892-L2206", + "version": "repo-lines-v1:sha256:8875e363badb600e00e7b80f354fca72b52ecd03e88a1c87cc770026fe10e7cc:eyJzZWxlY3RlZExpbmVDb3VudCI6MzE1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwMzVhNGQ1NGU3ZGUxYTdmYjE3YjU4OTllNWFkMjMwMWMwZGEwODlhOGVkYmMxMWU4N2NlOGViNzFiMzcwMGZkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI3NzJmOTY5NWM3NDY3Yzc0NzczYzljZTdmYzQyOWM1ZDBkMGU0ODdmM2M1MWQxNWQ2NjY4YTNkMjQwNzJiYjk1IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJjMTQyMzMyMGQ3N2NlMTg4MDY2OTBlZmRiOGUwOWZjNmU3MjZiODVmYjRkN2U2YTU1MTBiNWIxZGI0MDgzYWNmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI3NTg3OTY0N2FmZDExNGM1MTI3Y2Q1Y2ViNzcyODRjMjNiNzBkYTE5NDJhNGE2NzU0NDRkNmNhZjEyY2MzZTM0In0" + } + ] + }, + { + "id": "claim_824b46e4e601461a84a96f8f83ce7cbe", + "statement": "Chat models emit structured callback events throughout execution: on_chat_model_start (with serialized config and batch size), on_llm_new_token per streamed token, on_llm_end with final LLMResult, on_llm_error with exception, and on_stream_event for v2/v3 protocol events. Callbacks are configured via RunnableConfig with callbacks, tags, metadata, run_name, and run_id fields.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1668-L1676", + "version": "repo-lines-v1:sha256:1768cb9dcdfcc21004690fd656a760bc1260a60eb701db318f4c526426bcd74b:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOWJjYmNkOGYxMTBmODIzZGI3OTQ1MDAwZmE1NTI2ZjIxMDJmMDFiMmYzZjFhODI2OGE5ZmIzZmNmNWM5ZWRmNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTkwOWE1M2E2MThkMDI4ZTFkMzAwMGMwYTdhYmNlZWNiZTVmYjM3ZjRlNTExZWZlNDY2NDNmYWQ2ZTY2ODcyZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzU4MzA3NmJlNjk5YTdhNzIyYTYzYjNjNWY3NmVjMDc4ZWNkZWI5Yjc5YTg5NWQyZDc3MDg5MTZjMDliZWMxZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTY3MTViYjkwMWE5ZjIwMjU1ZWJkMDUzZGJmNDQ5NjkwOWM2MDgxNmVlODVkYzlmYTVmM2U2ZDBjNGYxZWVjYiJ9" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L773-L855", + "version": "repo-lines-v1:sha256:19a352f161e0454121917f747b7c73cde8e4ad68cb5844e6ac77c6b8aa425847:eyJzZWxlY3RlZExpbmVDb3VudCI6ODMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVjOTQ0N2JjMmFjN2Y0YTBhNGE3MzJiMmVhNGI1ZDIwNjYwOWNmOWI2N2NkNmY3NWUyOTFiYmQ1MDNiYzZhZTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRjZDM2OTQ1YzE5ZTA0MzBmNTg5MWQ1YzQ0NTczMTI1OWE5ZDgyMmEyMWZhYWI1Y2MyNmY1Y2UyYTZiNTRhOTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ1NDNiZDUzN2M1Y2FjN2NjYzQ3ZGVmOTE2M2ZjZWI3YWI5NWZhNTE1YTcxZWU1MWU5ZTZkM2IwYzhmODk5YWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI3MTgwZjZhYzIyYjU3YjVkMjU3YWExZjAxOWM2ZTY4OGU2ODEwNjE0YjgxNGE1NzgzZmU5ZWIzNzI0NWUzN2YifQ" + } + ] + }, + { + "id": "claim_4e7ce7ec11bc4a44b5bf0f404f70fa26", + "statement": "with_structured_output() constrains model output to a specified schema (Pydantic class, TypedDict, dict, or OpenAI tool schema). It delegates to bind_tools([schema], tool_choice='any') and chains through output parsers (PydanticToolsParser or JsonOutputKeyToolsParser). If include_raw=True, output is wrapped in {raw, parsed, parsing_error}. Requires bind_tools() implementation.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2385-L2565", + "version": "repo-lines-v1:sha256:673bc22e396265b7dbabfa67b802837132337352be622cd69c071cfc5dfd1c84:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiOTk3OTkzN2Q3ZGUxOTkyNzZjM2I3YzVkMTJkZWM2ODJhZTBmOTVjNTliZjA2OTlhNGI2OTRhZDg1ZWQxMTVhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI5OTUyMjA5NzQ4ZWJiMjJhOWM3Nzc5ZWEwMTBkODNiNzUzYmVlY2Q3ZDFmY2M2ODRiYTBmNDQyZjU4MDE4YzMwIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5ODYzYTc3NDFkMDgxOGZiZWMxOGIzODJlN2YyNzZjZTQxNGVlOGY1NGYyYzZjNWU0OGEzYTdkNDIzNDAxNzgxIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI3NTBjNDUzZDgxMjdmZGVmYWVkNTg3YWQxNGJlZDM0NWJiMGYzMTg3Njk3MTIwMWQzN2FjYmVkOGQ2NzgyZTFhIn0" + } + ] + }, + { + "id": "claim_fde81caf70274472a5807c28253e2d75", + "statement": "bind_tools() is an abstract method that subclasses override to bind a sequence of tool definitions to the model. It accepts tools (dict, type, callable, or BaseTool), tool_choice parameter, and returns a Runnable[LanguageModelInput, AIMessage] that includes tool definitions in API requests.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2366-L2383", + "version": "repo-lines-v1:sha256:a2c56a9bc7a84b7ed47b879ac55e9139606dc73bd2023a8ced246310d05fb38c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYxODU0YjA0NjkxN2UzYWIyYWQwMjllMGVlYWU3ZjkxZDBiZTNkNjkwMTRiYjYyN2FhZGRlZjJlNjlkMzg4MmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI3YjQ4Nzk3YmM1N2JkZGEzOGQ4MTE5NjhkMTE0YjgzNTZkN2I0YmUzMzFhMTQyZmY4ZDgzNWUwMzc3NjVlYzAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE1NzQzYjMwYzc5OTI2MzkwNGY4ZTgxODQyNDM3OWY5ZDIyYjJjMjAxY2I2ZTJmNzUzMzA0MTc3Y2YxYzk4OWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVhMTQxNjFmOTNhMDJmMzU5YmQ4MDVmZWI0Yjg5NmM0ZjExZWFiNGM1Zjc4NDA2ZmRjZjk5N2NlZGM2N2MyZGIifQ" + } + ] + }, + { + "id": "claim_a5942a521cca487ca5e1ca8f328163c9", + "statement": "The ModelProfile TypedDict (optional field on BaseChatModel) holds capability metadata: context_window (max_input_tokens), supported modalities (text/image/audio/video/pdf inputs and outputs), tool calling support, structured output support, reasoning capabilities, and temperature support. Profiles are auto-loaded via _resolve_model_profile() override in subclasses.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L378-L444", + "version": "repo-lines-v1:sha256:8cff3df6f9a49eb1b3a5ba8ab37f259d084d3c6082f7da23b16c453c1d93cd72:eyJzZWxlY3RlZExpbmVDb3VudCI6NjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIyM2Y0MDgzZDkzZTU2NzQ0Mjk1YWExNDU3ODNlNzYyMDhiNjIyYWJhYWI3NjhlNjliY2I3NWJmZDVhNGEwMGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyYWIwOTBiY2QxNjIwZmE2MmVhZWUxNTdhNzRmY2E2MzVhYTI5ZTEyY2JmZmQ2MThiNTc5MmQ4N2JjMDNhM2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImViYWQwMjFjMTkzNDNiNmEzOTYxY2IyMWE0ODRkYTg2M2Q5NWE2ZWRiYjI1ZTY1OWJjMzIzZTM3ODE5MzE5N2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI5MWFjN2JmYWFjZjE4YTk1NTM2YzVkY2RjYTc1ZDk5NTIzZmM0YmE0ZWUwY2FjN2IzNDQyMDc0ZmRlZjI5N2YifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/model_profile.py#L13-L150", + "version": "repo-lines-v1:sha256:497a9318b6379350b195e046e266ed2e8ccd7b0e2c69aaa337f7eab0811059ea:eyJzZWxlY3RlZExpbmVDb3VudCI6MTM4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmNjk0ZmRmZTIzOTgzZmQ5N2JiYTViODMwNTAzMWE4ZTU0ZjBkOWQ2YzhjMTI2ZDVlNzI5MjY3MmQzMDJjZWE0IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI2MjAwOTdlNGFhNTAwNjhiYTE1N2Y5YTI3MzlmYWQ4Njk5NWUxZTBjZGNjZGFkYzU4OTdjMjAzNGM3NGQzZWZhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI3Yzc0YzNlNWEzZDZjZDA1MTQ4NmU5YjRmNTQ3YzBlMjc3Mjk4NmUzOWQ4YmRkZDQ0MjU5OGUzZjQwZWJmZDhlIn0" + } + ] + }, + { + "id": "claim_af3159f29b2e4a53b9538a954ef8b5eb", + "statement": "BaseChatModel automatically loads model profiles via Pydantic validator _set_model_profile (mode='after'). Subclasses override _resolve_model_profile() to return model-specific profile data; the validator handles assignment and error suppression. Partner packages override _resolve_model_profile() to load profiles from their own profile registries.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L398-L444", + "version": "repo-lines-v1:sha256:f18d0b7106bc8237b8ffc290264d8d484ab6310f067a2375e15326297a177520:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2YWM3YjViZDJkY2Y2MjcwNjczMTFiYjhhZWQ1ZDk3NDBhODFjMmQ5NjhlNGM2ODE2Y2Y3MGM5NWMxZDQ1MDciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyYWIwOTBiY2QxNjIwZmE2MmVhZWUxNTdhNzRmY2E2MzVhYTI5ZTEyY2JmZmQ2MThiNTc5MmQ4N2JjMDNhM2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3NDAyMGJhYWEyYmU3OWY1NTVjMDk4YjM0ODQxMTRhNGU5MDU1ODA4M2UxNTE4OWRiMGJlMWJiNzE0YWYzYTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI5MWFjN2JmYWFjZjE4YTk1NTM2YzVkY2RjYTc1ZDk5NTIzZmM0YmE0ZWUwY2FjN2IzNDQyMDc0ZmRlZjI5N2YifQ" + } + ] + }, + { + "id": "claim_82543184d89c479aa3d772502f81c185", + "statement": "The disable_streaming field controls streaming behavior: False (default) enables streaming if available; True disables all streaming; 'tool_calling' disables streaming only when tools are passed. This allows gradual model replacement and workarounds for models with broken streaming.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L337-L353", + "version": "repo-lines-v1:sha256:51a6022ded251d8aee989262f4639c607e5697637e91e32e9b6adecdc3b77836:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjliYTQwNmYwNjc1MDBiZDdlZDBkN2EzMzc5NGE1MThhMTlkODFlMjJkOGEzNjMwYjgwNmUzZWM5NmJjMmQ0NmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA3NTliNzFmODYxZWZkYWMwNTM2ZGM3ZTljZWQwODg4MzFjZTQyZmI3MzdmMTg3MTFiODc3MGJlODE5Yzg2MzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZmZDY2ZTg0NDMwMzRhZTY1NmNlNGZjMzMwY2E1NTMzNjVjODZjY2Q0ZjRlZDJmN2ExNGUwMDRlNjk2NGJkZTUifQ" + } + ] + }, + { + "id": "claim_c8ecef7a6bac40fda27f1a0fa349d075", + "statement": "The output_version field (v0 or v1) controls AIMessage content format in stream() and invoke(). When output_version='v1', content is overwritten with standardized content_blocks format before streaming chunks and final generation. This allows consistent output shape across legacy and new code paths.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L355-L376", + "version": "repo-lines-v1:sha256:635a4b31855a4187743c7ababe83b7c78224ee57efa89c5d27dda7a74b5faf3c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2ZWZhODk1YTA1ZTdhNWZmNTNkZjQxZjJhZjA0YWFlMGY2ZmEzZDVmMzgxMDYyN2NhNDM4NmUxZGMxODEwMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJlYWM1OWEzZjE3NmE4NGVlYjFkYTQwMmY2MDNhZDNkMTFmMTIzYTI1MGJmYTRlOGFhMjI2YTE4YWNlOTJkZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU3NWY2YzU1ZDc4YzA3M2JlNmFlZjIwZGJhYWI1NzRhNjE3M2JmOTY4YTAwM2YzZTY0MWRjOGY3NWQ3MTZmOWUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L798-L810", + "version": "repo-lines-v1:sha256:17453abba43b78a6ebc60d0c77c65844c04edc5eaad2f8ccc01c73366e30dfa5:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjZWFjZmE2NjA0NDhkMTk3MDY4ZTZjZDY3ZDIxOGVlMjE1YTAzODFmZWI5MWFmMGZmODZiZGQxZWE5YzQ3OGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcxOGQ5MDEwN2I0MDBmODczNjEzYzI1NDNjMTQxOTE4NGYxZmM4MmZhN2EwN2Q3YjgyZTQzMTJlNzE5Zjg5YjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjYxOGQwMjljMGY5OWI2NDhlM2VmMWZmMGNkNDFhNTg5OGQzYTVjNTMxOTMzNDU0NDE4YWU1N2JkNWY3MmEwZWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE5ODZkMDg5NDUyMzc4NThlZTJmMTI4OTAyZTQ3ZDFiMjc2OGFkMTFjNWY2YTIzNWQ5OTIzN2RhNzQ0YTYyNTIifQ" + } + ] + }, + { + "id": "claim_43a30b4ed6ca4fc0aecb33ce2ff1bc07", + "statement": "BaseChatModel accepts optional rate_limiter field (BaseRateLimiter). When present, rate limiter is acquired (blocking) before _stream or _astream is called. This allows centralized request rate limiting across all calls to a model instance.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L334-L335", + "version": "repo-lines-v1:sha256:0fc1547d1be910cddd9353c31d20d343a35782557cdf49eca01c3c412bcb3314:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjZiOTA5MzdiMmFiMjE0NmE3YjAwZTI2MTY0ODEzYzhhOTdiMjkzYmVkMzg5Njk2YmRmZjY0NmRjOWRkNTRiMiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiY2I2MTY1NmEwOTU3N2E4MjgwOGZiNmZlMTI0Y2I4YTlhYTUyZGRlOTYyYTJmZTFmNzMwOWRiMDNkYjdlNWMwOCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMGQxMmRjMmI0ODFlMDk4MWU3ZmVmZmFiZTU4MWU1NzhkNDE5OTJhM2RiZTEyZWIzMWIzNDEzZTc5NzBjZjIxZSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGFjMDdiYmEyNGY5OWRiNjc2YzcwM2NhODA5NTc5MmY4ZTQ4Yzk2ZTQxZThmNGYzOWFmZmQ1MmQ2OTk5NjljNiJ9" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L785-L786", + "version": "repo-lines-v1:sha256:cf9be61ceadd3f7e68f7bd2e4d1cccd7602eadd3d5746d5d8380986d58f8cd4c:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNDQ3NDMxOGRjNzMxMTBlNWMxMmE2Zjg3MzBjMDIyNGRmZjUxMTEyMzFkMmIyNGVjYzVhMjg0YzE4N2NhZTQ3YSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjkxYmFkZmMzMmQ1ZDVhMDI4MWJjNmY0Y2JiYTM3MTlhYzQyOGQ3NTMwZjk0NmJhZTlhZjUyOTg2MjNmNDljMSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMTk0ZWIyMDI0OGUxZjJiODFhOTNhNmJiZjhhMGE1OWFiNGRiNGQ5MGE4YmY4YjQ2ZmI1Y2M5ZGRiOGQxZWNkZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGQxMzg1MDk5ZGI2N2U5ZDkxMGI3OTZjNWIzMWUwNDI0OGJlODc4Zjk1MmYzZGI2YTY3ZGZhYjAwOGJlZWE1NSJ9" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L916-L917", + "version": "repo-lines-v1:sha256:48256efe060f855bb4cc3b04cf89ff00b05eee9ec177da4b89bb9cc0c601f842:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzZmZWE2M2UxMGI2M2M2Y2Q5MTUxMDRkNTBkODcwNTUzMGI2MDBkOTllZjRiYWY3YWMxMzhkOGFhYTczMDJmNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjk1MWQ5ZjVjOTIxNDg2YmM2MDlmMjAzY2Q5ZGE0ZTBkOTNkYmYyOWRlZDJhNTU0ZjM3YTI2MjllOWJiZjZiMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDI3YWUxN2E2MDE4MDI4ZjJkMzIzZDA2Zjc0N2U0NTg3M2I0NjQzNDY4NDhhM2FiMGM2NTNlMzM4Mjg4ZjYwZSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDllYjAyNDY4YTZmZGUyYjVmOTg4NjYxMjVjMjA3NzYxYmM3M2JlMTJjOWYyNGM3OTgyMGVkZmYxZGEyNzRmZiJ9" + } + ] + }, + { + "id": "claim_9238f8d4394949909b8bdf20533210ae", + "statement": "Stream and generate methods normalize messages via _normalize_messages() before passing to _stream/_generate. Each streamed chunk and final message receive a unique ID (derived from run_id) via message.id assignment for tracing. A final empty chunk with chunk_position='last' is emitted when streaming completes.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L789-L827", + "version": "repo-lines-v1:sha256:ed945de6b40629a7372acc7b08a9bf7dce32fbadd5b2aaa7f0c7ab3354b6a9fd:eyJzZWxlY3RlZExpbmVDb3VudCI6MzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdhOWZhMmMwMmY0OGY0MmEwMDU5NWMyZGU3MzcyYmM0ODVlMGVlYzNiYWQzMGIxNDRmOWI4NmQ3NWRhYjMwZTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM1ZDMzMDIwODA2YmU2ZTQ3MDUyYTQ4ZDViODhmNmQ5ZTM3ZDUyMTRmNmE3ODAyNTQ2NzU3MDY5OWZhZjkzNzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQxOTNlN2Y0MTYyNGMxN2E2NzAyMzdmY2U5MWE5NmNiMTUzZDQwMDhlZjBiZjQ1ODhmNDMyMTU4M2ZjNWVmYTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY1YWYxOWJkZGMxZDJkZTA0ZjFkZjI5MWE1NmU0MGZlOWJiYWExMmY0NmRjYzU2MGZmMmI5NGRiMTYxNGE2YjEifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L922-L968", + "version": "repo-lines-v1:sha256:dc2eb57b0164229b93dad722773090979a244f7d8890a7b87859edaeb90d8fed:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3MDA1MzZlZDc5MmMyMGZiYTcwYzIxYzUyMWIzNDEzNzU4MzM4NWM4MTJiMjVlOGRhZmUwMWUwMjU3OGViNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZjMTU4ZDhlMDhhZGI3M2M5OGI3NTYyNjQyMDM1OTYzMjIyYjQ4OTNiZGFkNDJmZjM0ZTM3OTUyOTgxMDc0MWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNhNDRjZTIwMTQ0NjM0ZDUxZDA3ODY4MTY1ZTBmNDBlNTQxOGM0M2IzMGJmNTE2ZmFmNjY5OGJlNTU3ZTc5NzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcxMmNjMjY2Zjg3MWZkYzIzMWNlNmE0MDAyZmE3MTc0NmQyYTk2Zjk3NTQ0M2M0MGMzNDhhNDYxZWFkZjllMGEifQ" + } + ] + }, + { + "id": "claim_ec235df6cf964921ac38bf132ea90a74", + "statement": "Each generation accumulates response metadata in AIMessage.response_metadata via _gen_info_and_msg_metadata(), which merges generation_info and message.response_metadata. For single-generation results, llm_output is also merged. This ensures all provider-specific metadata is accessible to the caller.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2705-L2720", + "version": "repo-lines-v1:sha256:965fbb380f052092c2ab3b975d319f11de6c7062904ff58b40935569e5165aa0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYzZDQyOGIzNmM5ZjU0Zjg5NjIxZTM0M2EzNjE5NjgzNjhkNDYzMmYwMWU3MjdkM2QzNGRlZmU3MjYyYWUwM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIzZGYyYWNmMDZlOGMzZjAzYzcxMzVjNmU4YWM1YTljYTU0MzJhYmUwNmE3NGFjYTBmZGQ5YTUzYTAwOGY5ODciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZiYmY4NWMxOWMwMmNkNTBhY2FiMzQ4ZmJkZWU1ZjA5YmRmN2MyYTVhMmY5MWVkODlmNTI3YmZhZmVhMWEyZGQifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L797-L797", + "version": "repo-lines-v1:sha256:5cbd5b8f86a102c25aac35f771f6330bef7ce8d346a1a659d16f68807b8f42c8:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNWNiZDViOGY4NmExMDJjMjVhYWMzNWY3NzFmNjMzMGJlZjdjZThkMzQ2YTFhNjU5ZDE2ZjY4ODA3YjhmNDJjOCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNWNiZDViOGY4NmExMDJjMjVhYWMzNWY3NzFmNjMzMGJlZjdjZThkMzQ2YTFhNjU5ZDE2ZjY4ODA3YjhmNDJjOCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYzdjNjNlOWQ2MDJhMjUwZTVmMjgzYjA0NzI4MDk4MjhiY2Q3ODJhOGM2ZmI5NWUyYjUwNjUxYjc5ZWZlMTAyMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNWJmNDE1MDc4ZTU2NmI1ZmJiNWJkOTY2MjdhMDY5ZGVlZWUyMjhkODdiMTgyZDg0M2NmMGFiZDJlYTZmNjkzMCJ9" + } + ] + }, + { + "id": "claim_cc5fae439d664a6a8f8097b04d84aa96", + "statement": "_identifying_params (optional property) returns a dict of model configuration for tracing and cache key generation (e.g., {model: 'gpt-4', temperature: 0.7}). The _get_llm_string() method uses this to generate unique cache keys. Subclasses override to expose relevant configuration.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1578-L1590", + "version": "repo-lines-v1:sha256:db996df4a774482474945b91b56b5c878e43df72cc2fbf7c176428da7e1a4987:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0YmI4NzEyMzY4MThlMmU5YTg1ZjVmNGUyYjUzNTEzMzJiZGU3NDQxNDU2YTYwNmMwZGVkNmUzOGNjNzk5MTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlYTQ0MzllYmFkMjA4Y2U4YTM4ZjE3OGRlMjZiNDllMWRlZjdhOWRiNDhkYzhmNWMzODc0NGUxYzI5MTU2NzYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkzOTljZTliZTcyN2E1ZDJiOTI4MTM3ZWEyMTMwNmEwNjk0ODZkY2RhYzBjMmUyN2U4NTFiZDE1MmZjYmM3YWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQzYWI4ZjM2N2I3M2YzMjk5YWZkMTRjNzIxNmNmZjFjYmRlNTk5YjVhNGFhZjI3MDk3YjhlNGQwMjI0ZjYwNzYifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2329-L2331", + "version": "repo-lines-v1:sha256:7aadbc292065b19aaa0ec9ade9171595d53a81c258959cad724bc0714a97c76a:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjNkMDBjZjI3MDk2NGQ0NGExM2RmYWM4NzUyYjA5ZmNjNDM1YjBmMTliMzE2YWJjZjgxYTdhYTY0YmIxMjJkNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjQxNWFjOWY0ZGFjMzE3ODA2MzIzNmVkMTRkNzAwZTk4MDMyM2E1ZTJlMTAyODBjOTlmN2Q2N2NhOTQxNjAyYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYTI4MzFjODI4NzM0NWVlMzcyNTIxZTNlYWY5MzY0ZmUyNGYyOThkYjJjOTA2YmM4NDYyMWUyOGU2NDYwZWU0NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGEwMmJhY2JiNzI3Yzk0ZDk0ZmU2MDI3NTU3MmIyNGNlMzg5NzI3ODQ3M2NlYjRhNjg3MjYyZWRiOGI5YjRjMSJ9" + } + ] + }, + { + "id": "claim_6c79a894b9814774817788715384c997", + "statement": "_llm_type (abstract property) returns a unique string identifier for the model type (e.g., 'openai', 'anthropic'). This is used for logging, tracing, and serialization. All subclasses must implement this.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L2329-L2332", + "version": "repo-lines-v1:sha256:db535b12ac0a27fd6314901aaffafb6168ea80a9f313f96f7f93ffed50846b76:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjNkMDBjZjI3MDk2NGQ0NGExM2RmYWM4NzUyYjA5ZmNjNDM1YjBmMTliMzE2YWJjZjgxYTdhYTY0YmIxMjJkNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOGUyMWQ2ZjJlMjQwZGZiNmU1NzdiZGUzMWQxN2VjNDA2NjA3NDYxNTY2OGNkMmVjOTJiMzU0MmFlYWY5ZTkyYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYTI4MzFjODI4NzM0NWVlMzcyNTIxZTNlYWY5MzY0ZmUyNGYyOThkYjJjOTA2YmM4NDYyMWUyOGU2NDYwZWU0NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYzNhOWY1ZGU2MTcxMGM4NTc1MmFlODE2MTI4MWUwYWRhNjdhMjQ3ZTI0YzgxN2E2MzI1MTdhNWMwMmU4MGFmMSJ9" + } + ] + }, + { + "id": "claim_d3b5ed8dd982413fb716ce2501fe89e1", + "statement": "_get_ls_params() extracts LangSmith tracing parameters: ls_provider (derived from class name or overridden), ls_model_name (from kwargs['model'], self.model, or self.model_name), ls_temperature, and ls_max_tokens. Subclasses override to provide stable provider names and model identifier resolution.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1502-L1576", + "version": "repo-lines-v1:sha256:cb5ec623ca84e8c04a65697a7e85c757abc32cf3b201a5f819234d601aca8341:eyJzZWxlY3RlZExpbmVDb3VudCI6NzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI4NmM4ZGU0NjU1Y2MxMjg2N2NiYmJkMTdlMmZiOWI1NmYyNDM4YmJjNWMyZWEzYzQ4OGZhNjExYTViMjQ3NDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1NWI3ZjU0NDQyMThjMzUxNDk5YzM4ZDA4YmViYTMwYTViYjYyMGNiMDQxMGQ3ZGExOTU5OTYwMDEyNDcyZmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ3Y2Q4Yjk4MGU1YWE1Njc0ZjZkZWMzZTQzNjEzOGUzYjk1MDlkNzNlYzliNzJmZTgwNjA2YjMzMGM1YTdiNWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI4OTY1OTNjMDMwMWZiNzNkODc0NWQyNTVjNTM5NTgzNjcyOWE5YjZjYTg2ZTc2ZGRmMjU2Yzk2MTVhN2Q4MGEifQ" + } + ] + }, + { + "id": "claim_ca968af2819040e09edfeae44743ab8f", + "statement": "_should_use_protocol_streaming() determines v2 event routing separate from v1 streaming. Routes through v2 when _V2StreamingCallbackHandler is attached and native _stream_chat_model_events, _astream_chat_model_events, or standard _stream/_astream methods are available. Uses chunks_to_events bridge to convert chunks to protocol events.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L587-L643", + "version": "repo-lines-v1:sha256:2bfd684462989559832648a165a2f3dc126ce1f8c9950d17125e7bfa8905c307:eyJzZWxlY3RlZExpbmVDb3VudCI6NTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJlMzJkOTYzMzc4ZDEyYWFhMzVmYzI5NTg1Y2FhZjBkZGE0MmYxM2Q2Mjg0N2ZhZDdhZmRjNmQ2YTRkMDhjNTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYjY1ZTg0OTdhNWE2NDJmN2I5MDE4MjEwZDY1Njk4NDkwOWRmNmNmMDg4MzNmMzcyNjNjNmFkOWJmZTlmZTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc1ZjYyODhlYTM4N2E5NGMzODlmMTFlZTMzYzk3ZmIwZDZlNjRlODlkNTczNWQ2MzlhMTkzMzUzYTIxZjEwMjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ2OGQxY2FkM2Y5N2UxMzk2MDZlYzlhOWQ0NTMyYzExZWNmZmY3MjMxMmU5ZGM5M2Y3YjU3YzZmY2U4N2ZlZmEifQ" + } + ] + }, + { + "id": "claim_3bdb8e7e3d224335979da820afec7a0e", + "statement": "_chat_model_stream_v3() returns a ChatModelStream with lazy initialization. It wraps event iteration via _iter_v2_events and uses a pump mechanism (pump_one) to lazily consume events. Rate limiting, event collection, and lifecycle callbacks are managed via closures with shared state.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L994-L1133", + "version": "repo-lines-v1:sha256:ce77e9c13ab68c17193b88c83c469b90ada207031c26fb7d00ed822f15010d46:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwNTY0MGNlY2IxYjc3YWNjYTM2MTU0MjZlMDdkNTQzNWZiMDllOTMzMmU1ZjRiNjFlZTg5MDU2YzQzNjNiYzJmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyMmU3M2VlMDNmNzM5OWVhYjExMWEwMjE5MWZmYTc5MzJhOGFiYmU5MWFkM2NiYTM1Mzc0M2I2MDI4MjI4MGU2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5ZGYxYTdhNTVlNzBlNDQ2YzhjY2RkYzkxZjRjOWY2ZDg5YTc4ZmVjMDNlZTQyN2U1MTA5MWVkOGRhMmJiYzM5IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiYmJlNTNhODc3ZDc2ZGI4ODA1MTI0YzYzNjcxMDM2OWI3YWE3YTliZjFjYWVmMTEzOGQ4MzEyMzY4YzA1MTdhIn0" + } + ] + }, + { + "id": "claim_78536e5312b04ae1a69c7bb45a2d0ed5", + "statement": "generate() and agenerate() accept a list of message lists and internally batch-process them. agenerate() uses asyncio.gather with return_exceptions=True to run _agenerate_with_cache concurrently. Results are flattened and combined via _combine_llm_outputs().", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1592-L1716", + "version": "repo-lines-v1:sha256:10713381eddb3932f6ee9ae086bc1e0e35fde2b65c37de93be4f7d5fa361ff20:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiMmRmODNlNjhkYWVkNjY4Mzg5MGU3MWRhNDZkNWJiZGM2NDZjY2QwZjEyNjI2YWM2NTNhZTJhN2FkNTUyOGQ4IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI4MjBlN2JiNmFkMzZjNjE5YWIxZjJiM2MyYjQzNTVjNjFlNWI1NTc3MmM4MmU2NGU1YmVkZmZkZDQ5YzQzNzMxIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIwOWQyZmY0MjNmMDhiMDAwMjY1ZThhOTQ5ZTZiZjA0ZjhlMmExMGI1ZWFiMTU4Nzk0ZWZmMWE1Mzk2OTI4ZDMwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJkZDQ5Y2Y2NTVhNWI1ZDVjNGFmMzRmMDBmMmQ1NTIxMWZjMGJiOWQ3NTAwYTEyY2Q3YWNiMWRmZWI2MzY0MDkwIn0" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L1718-L1866", + "version": "repo-lines-v1:sha256:f4304f6ddc44aee6a4ec1f81b165dea18be17bc0077e61b0c0f4e0d4d857b4f7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQ5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyMjlkZWYwOTY1YjQxMjM2NDI4ODNkNmI4MmZlZTJjYTNkZTI2NjlmNzVhNTZjZjJlM2IyMjViYTQyODEzZDQ3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI4MjBlN2JiNmFkMzZjNjE5YWIxZjJiM2MyYjQzNTVjNjFlNWI1NTc3MmM4MmU2NGU1YmVkZmZkZDQ5YzQzNzMxIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI1MTRhZTQ0M2ZhYzNjNjEzNDk1MmIwNDU0MTFkMTRlYjQ0OWQwOTdhMjExMGMxNDFjYTYwNjJiYzdmMjM3MDNkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmOTQ0NzZhYTQ5NzY1OTI4MzBmYmQyNjRkYWEwNWFlOTczM2U0ODBmNmU1MTU1NTNmYzllMDU2NGFmMjVlN2U3In0" + } + ] + }, + { + "id": "claim_b18ce93e72c247ab8ad3f21f5123766c", + "statement": "LanguageModelInput is a union type: PromptValue | str | Sequence[MessageLikeRepresentation]. _convert_input normalizes all forms to PromptValue for downstream processing. String inputs become StringPromptValue; message sequences become ChatPromptValue.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/base.py#L140-L140", + "version": "repo-lines-v1:sha256:003db345d026fe447522acfcf47a5f6bc994ef5f32fcb613cf3bb3427b212583:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDAzZGIzNDVkMDI2ZmU0NDc1MjJhY2ZjZjQ3YTVmNmJjOTk0ZWY1ZjMyZmNiNjEzY2YzYmIzNDI3YjIxMjU4MyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDAzZGIzNDVkMDI2ZmU0NDc1MjJhY2ZjZjQ3YTVmNmJjOTk0ZWY1ZjMyZmNiNjEzY2YzYmIzNDI3YjIxMjU4MyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYTE3ODJiM2Q1N2U0NjU4MjhhYjBmMDFkYTMwNGE0OTFkYTE0MDBjMzRlZDdjNzY3YjU1YjUyMWU4NTcyMTUwMCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMWE5MzI5OTJhM2E3NzYxNjA1MGFiNDBmMjhkZTFmYmJlNjVmOGI4OTk2ZmU4YjllYWY3MWVjNTJmMDFhZGUzYyJ9" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L461-L472", + "version": "repo-lines-v1:sha256:24287b0abdaea0feab7ef708f33a5c3586ce8ca68f42123671e789ee6437258d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImExY2Y5ODYxZjhhMGI5MGJkYjMwOTUzMmI4NGVmMTIwMmM5Y2FlZWM1Y2Y1NzAzYjEzOTk1Y2M1YzUxYTg0ZGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2MWUzYmZlZjJjZDZhZjhhZTUyYmYxYzNiYjllM2NiYTViYmYwZDI4ZDg1MTBlM2YzYWFjZGI2MWM4ZGJhNWQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI2MTA1MDc4YTkyMGM3MWQxNzFmYjk5ZWJjYTQ1ZTM1ZWYyZjE2N2QwZWY2MzU0OGNhMTYwMmRiMjViNTczYjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI3N2VjOGI3OTAxMWU0ZmM0Yjc5OTQ3ZjYxYmFkOTdhYzBmZGRlYmRjZWVjMjc0ZGE1ZDM2MjMwMGQyYzYxNTIifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/ci-workflows.json b/openwiki/.claims/ci-workflows.json new file mode 100644 index 0000000000..a84c6ce738 --- /dev/null +++ b/openwiki/.claims/ci-workflows.json @@ -0,0 +1,242 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:8af0cd4e2f4fde3bbeaa007c68ac7b8049bd352e75e6455e2b7896818da707cc", + "claims": [ + { + "id": "claim_cb5fe3f72803497eac3f09ebf02d97cd", + "statement": "The primary CI workflow (check_diffs.yml) detects changed packages via Python script analysis and generates separate test matrices for linting, unit tests, Pydantic compatibility tests, integration test compilation, VCR cassette tests, and extended test suites before running corresponding downstream jobs.", + "evidence": [ + { + "resource": "repo://.github/scripts/check_diff.py#L1-L54", + "version": "repo-lines-v1:sha256:97ef035504344bb0cfaabad9e93e0f1bd4eee1d885a2029155694f8224408c21:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxNWQ2Y2NhMTkzMjJlMWUwYzg3OGRmNzc1Nzg4ZWRkOGQ3MTFkZmIyODBlZmI1YWZmMmQ2MmU0ZDJhMDE4YTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUxODIxZWQ1N2E4NzZiZGUyYjM0OGFlYTFhYmRhNGMzNDgzM2M4MTkxYWQ4NTM4NWZmZmUzNzQ4MzM2MDlkZDkifQ" + }, + { + "resource": "repo://.github/workflows/check_diffs.yml#L43-L72", + "version": "repo-lines-v1:sha256:90f6935adda3467ddbea42901236cf760dca30eb3ad45c339ec78037287536d1:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlZTNjNjBkYjI1M2NiY2VhNzI0YWFkZDkxOGViOTQ2NjRmN2M2N2U3ODRmNzk2Zjc4NGNmMTAyMjA3ZDI2YmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMwM2QyMGIzNzVjNzU1YTk5ODI1MDNjNTI0NmY0MjViNjk5YjIxYTJmYzZjMzE3MjdmMzhjNjAzM2RiODE5NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjYxNTdmYTE2N2Q5ZmU2YmIyNmRjOWMzNjdlZDkzOWIxNjM1MDMxM2E3YmIxMTUxNmYyMDUyODA4MTA2ODIyYzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhZTBmZDMwNDk3Njk1ZDM0NDMyZWI3MTA5NDk4ZDM1N2YzMTUxOWU0MjFkOGUyZGRmYThhYWUyYjA0ODI1MWEifQ" + } + ] + }, + { + "id": "claim_988c08f335474462a1ffcecc3ce9233c", + "statement": "Linting (_lint.yml) runs Ruff code analysis with GitHub annotations, MyPy type checking, and markdown linting on affected packages, sourcing tools from lint and typing dependency groups.", + "evidence": [ + { + "resource": "repo://.github/workflows/_lint.yml#L1-L81", + "version": "repo-lines-v1:sha256:fb33c9af65a879efb7ceb1f73f66585a6ef71217ab9d2cc3648aba80673cc25a:eyJzZWxlY3RlZExpbmVDb3VudCI6ODEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJmNGY3ZWEyNDA4N2E4YTk3ZjdjOTZlMTBlZTJlOWRlMmY0OWRkOWQ5NGJmMThmZjc5MmVmNTMwZDZiMTYzMDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxMDM2ZTY3ZjMxNTRiM2Q5YWQwZmIwMTYzMGI2ODk0YTIwYmFmNmRkM2QwNmFkYWJjMjIyNGZkNjg4OGIwOWYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_6b0fc2411ecf4fad88fca965449de834", + "statement": "Unit testing (_test.yml) uses matrix testing across Python versions with two-phase validation: runs against current locked dependencies, then downgrades to minimum versions calculated from pyproject.toml constraints and retests to verify compatibility.", + "evidence": [ + { + "resource": "repo://.github/workflows/_test.yml#L1-L85", + "version": "repo-lines-v1:sha256:c8b66a87e4f78fad14d6132b89a46335d8ea8de37901955bf8c28c2847549d66:eyJzZWxlY3RlZExpbmVDb3VudCI6ODUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0YzM4ZTRiNTdjNjcyMzg1MTkxYWM0NGVkYmMxMjgxZTRkNTRmMzllZWJhNmQ4NDk0ZjA5NWM1NWNmYzRlMzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNjMzMwYzU3MjBjZjQ4Mjg1MGQ0NWFkY2IyZmQ1MmVmNzdmY2VmOTkxNWY0MDE0ZWEwMjJhY2ZjMTU4MzEyMTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_faff1fd3ff6a47dcbe788006e8099848", + "statement": "Pydantic compatibility testing (_test_pydantic.yml) validates affected packages against configurable Pydantic versions and reruns full test suites to ensure multi-version support.", + "evidence": [ + { + "resource": "repo://.github/workflows/_test_pydantic.yml#L1-L73", + "version": "repo-lines-v1:sha256:55c42873c6aef62b5096d18f8211187bf1253f9e316f3daeae38863bcd5f8fd8:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg4MzgzOWE5Yzc3ZThiMDgzZDlmNGEzYzU5ZGQ2ZDhlNmE0ODNmNzVmZmRjNjM3NDkwZTcwNDJmZjQxMDNkMWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNjMzMwYzU3MjBjZjQ4Mjg1MGQ0NWFkY2IyZmQ1MmVmNzdmY2VmOTkxNWY0MDE0ZWEwMjJhY2ZjMTU4MzEyMTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_8400626cf3504ce09a31ece9a4afdf77", + "statement": "VCR cassette tests (_test_vcr.yml) run in playback-only mode with fake API credentials to detect stale cassettes from test changes without re-recording, triggered only for packages with recorded HTTP cassettes (e.g., libs/partners/openai).", + "evidence": [ + { + "resource": "repo://.github/scripts/check_diff.py#L36-L40", + "version": "repo-lines-v1:sha256:33a13b78d3cd4c5fe97e291d64efdccc6f28035e8daf21586d55a46413fafe09:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg0YzkzMDhmNzdjMDlhZmNmYzI2Mzg2YTk2ZTc4YTFiY2I2M2U3NjkzN2MyZjNjMWQ5N2M3ZWQ5MDEwNDY3NCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDEyY2EzNDVjY2Y3NWJmOWMwODA2YmNlNjk1YmU4ZGU4MDhiNzk5ODQyNTFhN2E1NGQyMDJjZjYxMDFkZDQ1MSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMTFjNzAzMmM5MTk3MzRjMTAwOGYzN2MzM2QyOGQ0OTY0Y2Y1MzdhZjMzZDhiNjUwNTBmZDE5MzlkNGFmZWMwOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTBiMTFkMmY4NzU0ZTVlNTRhYjcyYTk4ZDliOTNlNmE2MjY4MjJmZDBmYmIxMjVhYmUyOGE3Y2M4YzYwMGQ4ZSJ9" + }, + { + "resource": "repo://.github/workflows/_test_vcr.yml#L1-L66", + "version": "repo-lines-v1:sha256:69050e686a468c8ea429a563b7050fe40dd39acb7ab90334b481bc7bc0a37762:eyJzZWxlY3RlZExpbmVDb3VudCI6NjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY3OGRiODQ4OTFlMzViNzFmYTUwYmQ5N2YyMzJkMjNjNDFkMGNmNjkzMTM1ZDQzNjA2YTc2NGY1Yjg2YjU0ODMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNjMzMwYzU3MjBjZjQ4Mjg1MGQ0NWFkY2IyZmQ1MmVmNzdmY2VmOTkxNWY0MDE0ZWEwMjJhY2ZjMTU4MzEyMTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_91801b10b92a47e799b60f91d2a924ad", + "statement": "The release workflow (_release.yml) separates build and publishing jobs for security: build job (no credentials) runs version checks against PyPI and creates distributions; publishing jobs run in separate security context with trusted publishing tokens.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L140-L283", + "version": "repo-lines-v1:sha256:15929f1a5b9ae728de64ae6350f0e5b12c240077b6deccf9cfde4228b324a302:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQ0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI2MDNmYjA5YzI3MzNmMTZmYjg0MGIxYzY1NTJkNWQ0MDI0MGIxNGUyYWE2NjRhNWQ0ZDFlYWQ4NmI0NzM5MGIzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0NzBjOTQ1NmJjMWM4YzA0ZTE1YWEzNzJjNjQwMDlkMDQyOGJjZjNiOTJjMmI3NjJlMTRiMDZlNmE5NzlhYTRlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI4OGI0MDE0NjU1OTY4MzE5N2VjMjU3ODkxMGUxN2NlMWZiNTNkNWJlYTE4OWU1NWMxNTk3OGJkYTJmMmE5ZWJlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI3MzFhZTI5MjZjMmVkNjc4MDVhMzc1ZjMzZWJhYTI2Mjk1YTY2Mjg2NDgzN2ExYzQyZmRlOGIxY2UwZmU4Y2E3In0" + }, + { + "resource": "repo://.github/workflows/_release.yml#L263-L274", + "version": "repo-lines-v1:sha256:8fac379434387b7ff418ee80553928eb371547752ab5bf4bebfcce50325c1384:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQyN2MxYTlhYThmZTQ5MGE4ZTZkNWE2MGExNTNiNzgxN2FiNjVjODcwNDRjZTExMzQ5MjZlOGVmMDJmNWMyMDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkNWNlNTAxNDY3OWU5YWY2MWQ2NWJlZDhiODllNGQ3YzZlMzQzYWM0NWZkYzEyYTZlMGFmYWJiOTM3ODc4MTAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcyNDgwOWU3NzY4YzUzNWVmMTQzZmJiYmZkMTUzY2ZkYmM0MjY0NzU5ZWYzM2Q5NjY5YWI0MDcwMmMyNjAxNTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhiOWE0MTg3MjBkYzI1ZTU1NjkzZWZlZDExODJmOWI4YzEzYzcwNjIxOTE1ZDZiOGNjOTVmNWJkOGRkY2QxZmYifQ" + } + ] + }, + { + "id": "claim_26b6ae2237874f2abd29497bf5ea977c", + "statement": "Release workflow build step checks if package version already exists on PyPI using PEP 440 normalization (e.g., 0.1.0-rc1 and 0.1.0rc1 resolve to same release) and fails if version is unavailable, PyPI is unreachable, or version mismatch with pyproject.toml.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L189-L261", + "version": "repo-lines-v1:sha256:218aecbec90b654052ca3fb2fc75a1eb6080533e7d1cb1a53a6785c9a8d63164:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk0YmM5NmU3NzNhNjYzNjgwZjhhNjM0OWE0ZWM4NWMyMWEwNzVmYzExMmVmMGQxOTg3YzQ5NWI5NTE1ZTBlYTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNiYjlkZTNiNjhlZmYwYzVkNmY4NjdjMGIzOTAzZGJjMWI4OTQ3NzhlYmRmYmZjZjk1OTc0NDQ4OGM5Mjc3NDciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2N2IzNmQxODRhOTEzYTU0MjBjYmUwYTdhZGRjMjBjOGMzMTQ3YjVlNzMxMTk3YmNjODMzYmRlNjJiZmQyYWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc0OGI4NTExMzMyNGY4NDcyMzc5NjU0M2NhNmI2YWFhNzU1ODQ5YmI0MGMzYjRhOGJlNWIzOTRkMmFlODgyZjEifQ" + } + ] + }, + { + "id": "claim_46fdd738a1ab4b7e968a67aaed75c159", + "statement": "Pre-release checks (pre-release-checks job) disable caching to catch missing dependencies, import the built wheel directly, run unit tests on both current and minimum dependency versions, block prerelease dependencies for stable releases, and run integration tests for partner packages.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L383-L540", + "version": "repo-lines-v1:sha256:a104fedce8750e7ab5723c475b0b13ebfd3f3d796baf2019da078b2ec4acc729:eyJzZWxlY3RlZExpbmVDb3VudCI6MTU4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIzNmI2OTNjOWRmMzU1MThmNzEyODQwN2UyNDhiODM3ZTAxZjBlZmJmNTQ4ZTg4MGUyMDA4NTgyZDdmMTI3MDdmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzMzIxNWFlOWJkMDZkZTVkNGE1YjYyZDYxMjhkOTZlYWI1MGVjNWZjMjQ3ZGI2YjAzOWFlMzcyYTYyNTFlZTRmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIwM2YwY2ViYjhlODY5OTBkMjBiMzc5MzljYmI1NzYyMDUwNmU4ZTJiZGNhOGU2NzBlZjVkMTczNTQxYmVlMDkzIn0" + } + ] + }, + { + "id": "claim_ef566ddb3bfd47eca370bcbe31b19913", + "statement": "Release notes generation finds previous release tags via git history with special handling for pre-releases (matches base version, falls back to latest) and stable releases (searches previous patch, falls back to latest), then extracts changelog via git log between tags for the working directory.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L304-L381", + "version": "repo-lines-v1:sha256:02c16c867a48d496d5d31cc9a7860ed52a43ba2fb5583c13d915e49e06fc323e:eyJzZWxlY3RlZExpbmVDb3VudCI6NzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0YjE2ZjJjYmZkYzMzNWVmZmU2ZTQzYjI5YzcwY2ViNjRhNWRjNWRiN2Y1ZGUyNGJjMTllM2Y2NjAzODI4NGEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU0Mzk5NDc4OWM4MWE2ZGFlY2IyMWQzOWU2NGZmYTliODA2ZjczYWFiNjQzMTQ4MGFmZmY4MzE3MTExNTA5ZTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI5N2UzYzIxNzRlYWFiMDBmMmFkZjI5NGVmNTEyZjY1NjU5MTkzZTJlMTg4N2RiYmMzZjI2N2UwOTc4YmRjZjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU2M2E0YWRjZjllMjgyNTNjMTM3ZjQyMjA4YmVhNTBjYzNjNzY0YjQwNDcxMWFhNDE3NWJmOTc3MTg4MzhkMDAifQ" + } + ] + }, + { + "id": "claim_5ef1eed172f74ac6ab61e4128ed815e8", + "statement": "Integration tests (integration_tests.yml) run daily (1 PM UTC) with matrix over Python 3.10 and 3.14 against 9 default partner libraries (OpenAI, Anthropic, Fireworks, etc.), receive 30+ API credential environment variables, and serialize per-package tests via concurrency locks to prevent credential conflicts.", + "evidence": [ + { + "resource": "repo://.github/workflows/integration_tests.yml#L152-L250", + "version": "repo-lines-v1:sha256:f171d5b939a4417dba2c2ba55d219c4555bc150a7422e4b0fdd0ee5a3c902593:eyJzZWxlY3RlZExpbmVDb3VudCI6OTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjZTUzNjI0Nzk1ZDA0YmIxMjI2ZjI1NTgxOGUxNmRlZWZhMGU1ZTVlODYxN2I3MDZkMjAyNzA5MWYxMGE3ZWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNkNGQwMzc4MWVmYzEwM2VkMTMzNTk1YWFlMjlmYzI4NDdmMTBkYmQ1MjAzMWFhMTZiNmYzYTI0ODdjNDA5OTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE3ODljMzlkMGFiNzhkOWFjNzRjNDBiODEzZGRiNGNkYmRlMTc5Mjk0OTY4M2VhYzVmZTZiYjkyMDgwZTc2OGIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRiNjU0ZGI5YTFhZjZiYTQ1MDQ2NDViODQxNWM5ODZjOTlmMmMzYTU1YTUyMzgwN2IwMmRhZDllMjM1Y2Q5OGQifQ" + }, + { + "resource": "repo://.github/workflows/integration_tests.yml#L56-L173", + "version": "repo-lines-v1:sha256:1a8edf16cffab1fed529e43b9a7cbacb77d936c43c70e0101b5774bbd60da086:eyJzZWxlY3RlZExpbmVDb3VudCI6MTE4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5Mjg4NDBhNmI3ZTYwMzAzZDg3YmU0ZDRkODJhNTBiMzMyODQyOTBkZWM0ZTM0MjhkNDdkOTNmYmVmZDFjNTkwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhM2M4NzQwMDg5NmU4Mzg5Njk2M2RjM2M3ZjBlZDU1ZDVjOTJlNGUyNzI0M2QyMDk0NWQ5NjhkMzdmZWExZDk5IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJjNjhjNjBmMmRiMTY1NGJlMDI3NzQ2MDJmMGFlNDU1N2FlNjM2ZTNiYzU3NTI0ZGJlMDMyYTNmYjlkMjVmMjFkIn0" + } + ] + }, + { + "id": "claim_97644e1aaa3e49bda3b645ed2478cf80", + "statement": "Integration tests handle external repositories (google-genai, google-vertexai, langchain-aws) by checking them out separately and reorganizing into local partner directories, with special per-package install logic for overlaying local editable core and standard-tests packages.", + "evidence": [ + { + "resource": "repo://.github/workflows/integration_tests.yml#L179-L248", + "version": "repo-lines-v1:sha256:c2e36b44b35a4586726b41e28ad4ec1f830ea00101b910b4d2f88368782174b0:eyJzZWxlY3RlZExpbmVDb3VudCI6NzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc1NjM5ZDFjYzRlNzE2NmUyYzlhYjNhM2Y4YTllMmIwMzNjYjcxNDZiYWU2MWU1YzZkOWM4YjIwYzEzYTNlZDciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMyMzA3MzdhMjFmYWIzYjUzMjU2OWUxNGJkYTkyMzc0YjQ4NDc4NGNhZWZhOTgyZDIzYjU0ZjRhNjhjNDc4YTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk0NmZmMzU1ZGZmZDExNTNiMDQ2NzhiMGIzMDVjNDBhNmU0MDNmYjk1YzEzNDUxNzZlODRmNTEzZTQyZmFiNjYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVjNTZmYTBhMzEzZjc0ZGRkZTRkMWM2M2UxY2RmMGJhMWExMDQ4ZTY3NmQ4OTI0YTZjOTNjYmEyMjc4MTNiNDYifQ" + } + ] + }, + { + "id": "claim_13b0caedf05f4f34b2d369323d87c866", + "statement": "Issue auto-labeling (auto-label-by-package.yml) parses Package section from issue body supporting both dropdown and checkbox formats, maps package names to labels via JSON mapping table, and adds/removes labels to match selections.", + "evidence": [ + { + "resource": "repo://.github/workflows/auto-label-by-package.yml#L21-L100", + "version": "repo-lines-v1:sha256:fca7a2ef5c5deb22252ab3af123e2380ec861fcd17f0a9cef1c1bd4f9f5f1403:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiYjA1ZmE0YjY4NTVmNDc1YTc0NDA0OTljN2JhZjNiZjk3OWMzNWZiYWNkZDhmZGU1ZTlhMTgzOGQyMTUzN2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMwMThjMzQ3MmE5ZDMxNWIyNTllNDQyNDMxNzRhMDQyYTNiZjk4ZjQ1ZDUyNjg3YWFlZGExNDgyNDVmYWNiMTAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE0NGJiNTllOGRlZWUxOTAxZDNjMWM3YTIyZjM2MDU3OGRkNTUwOTU1OGM0MzA2NzU0OTg0MzU3NWUyYTFjZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ5MjU0ZTEzNzQ3YzczYzAyNDZhNWRjNzg3MTNiMGU0NmZmNTQwMTdiZjRiMjQ3NGUwNmEzMGZkY2U0MDllODIifQ" + } + ] + }, + { + "id": "claim_ecdce57a4c5d4850abc2265c1d57a0c6", + "statement": "PR labeling (pr_labeler.yml) unifies file-based, size-based, title-based, and contributor classification into single workflow using concurrency locks to prevent label race conditions, and uses GitHub App token for organization membership verification.", + "evidence": [ + { + "resource": "repo://.github/workflows/pr_labeler.yml#L1-L80", + "version": "repo-lines-v1:sha256:2dbf45e00dd63b3efde21201c64129b4fb2a233aa9265820ff396a5d20ed94ac:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ5ODFlMGVkYmMyMWFhZjc5NjFjZTNhZGYxMzM4YmJjMWE5Njg4OTZlZjkzZDlmMGRjNjE0MDBmNGZkOTMxMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyYzdjZGFmMjc5MjJlNTU0YTUxNTc1N2JkNjExMjJmOTAyZmNiMjdkNTQzZjg3N2Y1YTc0NjMwZjRiYWY3ZTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUxNjQxYWY5NWE0MDhhM2Q2YjNlMjBlOGRlZmE1YmEyNmQ3Zjk4ZTMxMDYwZGI4MzY5OGZjNWVkZGU5ZTE0N2IifQ" + } + ] + }, + { + "id": "claim_36db0f10906f4354881a102930352456", + "statement": "OpenWiki auto-update (openwiki-update.yml) runs daily (8 AM UTC) using full repository history and openwiki CLI to regenerate documentation, creates pull request with changes, and preserves partial progress on failure for baseline establishment.", + "evidence": [ + { + "resource": "repo://.github/workflows/openwiki-update.yml#L1-L77", + "version": "repo-lines-v1:sha256:bd561d495ac81334fdfcf2b4984cd8b328074ef526f45bb4f2fc5e6598f4c75d:eyJzZWxlY3RlZExpbmVDb3VudCI6NzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA2MDI3ODdhZjZkY2MwODM0ZGUxMjFjYmIxY2I5NWY5NDU5YjcyMWVhYjAzNDlmMTA1MDFiOTk2YzFjNzdmZmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM2NzE4ZjA2N2I1ZTVjMWYwOTkxYTZlNzQ3MjZmZTJhMjNkMjY0MzQ1ZTA4ZTRlYWQwNGZhZjczYzI3ZWQ0NDMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_6b65a0ec4bcf43e081e47dc1519c1908", + "statement": "Minimum version testing uses get_min_versions.py script to extract version constraints from pyproject.toml, query PyPI for earliest satisfying published versions, and downgrade dependencies for retest; supports two modes: pull_request (lenient) and release (strict) for different CI contexts.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L470-L490", + "version": "repo-lines-v1:sha256:189ad3f1c987c3d09705339f603b9284e4c2833c8e29a910602c4a3838e9c3d4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFlZTZiYWEyMzFjNDUwODllY2ZlMDYyYWNkOTNhNDAwYzQ4MGU4M2NmMWU1MTdjN2M3M2M2OWY2Y2RkOTI2OWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFhNTlmZGQzZDgxZmQxNTJkNTllMmI3OWJhMjE4Y2YwYmEwODJhZDY3MzdmYTdlMWJhMTBiNGVjYWUyOWRjYjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyYjY3YzFiZTU1ZTZlNzc1NGRlNDQyYTM5ZWJjZTI4MWM4ODgxOWFkNDgyMzU1MGRmOTY2NTY2MTU4N2UyNGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzMmM4NWUxNzFlYmVlZGNjMmI3MjY4OTNhMjEwMDY4M2M5ZDJkY2QzODRhZDE0MGMxYWMyYzZhN2IzODAzZmUifQ" + }, + { + "resource": "repo://.github/workflows/_test.yml#L55-L72", + "version": "repo-lines-v1:sha256:a106ff2b4f7b419ad92fafd1a672286d2a8c523778a6787b41c4ed71be9b1b2f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkNzQxZTBhNzYyMDMwZDAzMzkxYjY2NzE3OGU4NWYzMWE2OTA0MmU1MTE0MjkzZTBhZTBmNDk1ZjNkMjliM2YiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMwMDU5OTg0MTZjZTRkMjk2ZDJlODk3YmI0MzMzZWQwMjQ4ZTNjNmNiOWJlNjFiMjUyOTljYTZlNTFiNzk1YWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ1M2JiMTZlN2NlZmVlMTFiY2VjOWU0OGU0YWRjMWRiZWUyMjllMzY2ODllMDJlOTkzMTc1ZDY5MDNhMDk2MTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU4MzBhNGM0MDFhMmJhY2IyMjYyODAzZDAzYWJjODYzMzMyMzFmODM1MWFiZTEzNDMxNjM1OTVlMGY4ZGVlZmEifQ" + } + ] + }, + { + "id": "claim_6f0e8ca64f5a4dc08279158e5dabc14a", + "statement": "Release workflow for core package includes test-prior-published-packages-against-new-core job that fetches latest published partner packages (e.g., langchain-openai, langchain-anthropic), tests them against new core wheel, and can be selectively skipped via skip-prior-published-package-checks input.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L581-L743", + "version": "repo-lines-v1:sha256:c0f08801cbfc52c661f05da45eb66d15291617ff791f5b189ca1636c97b0a75d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiZTE0NDc5OGY0ZWI3OWMwOWU2YTlkODRiODNmN2FjOWFhN2M2ZjMzNzNjNGM2ODU1ZTBmZTU0NGQ1NjJlYTliIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiMzBmZWQ1ZWJjYzc1ZDJjMWQzYTA5MWRmOWE0NTlmNzY1ZTkxMGY2OGMzODllODE5YmU1ODFiNWNkYzQ1Yzg1IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxNTkyOTYzNThkYzlmZGU5OWNhMmEwMWYxZDdiNzg1NDdmMjgxMzVjZWM0Yjk5OWI3M2IwNGM2ZjVlZDU1NzY5IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIwYmMzNDA1YWRkYmFkNmFiYzExNmFiOTg2ODY2Mzg3NGMyYWRlNTI0ODM1NTE2MGVhMTBlZjQxZDdlZGU3NjkxIn0" + } + ] + }, + { + "id": "claim_49d056d38efd434781bfabcd9b68407a", + "statement": "Release workflow includes test-dependents job for core and langchain_v1 releases that checks external dependent packages (e.g., deepagents) across Python 3.11 and 3.13 to catch breaking changes before publishing.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L745-L800", + "version": "repo-lines-v1:sha256:76558b4fee1fb23d87c29da42dd3aa55372e78ca93032808f0856ce4261d1dc5:eyJzZWxlY3RlZExpbmVDb3VudCI6NTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkzNjZjNzE4YmMzMzk3MTUyNWQ3YjdkNTY2ODhmZTcwOTdiMmQwZTgwYzJhMzc5NjE5ODEzNDQ4OTM5ZDVmMWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVmMjRkYmFmZmY0ODI0OTFiMjA4M2I4Yjg2MGE0ZDMxZjkyYjUzMzFjMmUyMzhjODEwMzZkNjJhMzZjZTM0ZjUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFjMmY4OGYyNGY0ODFjZGNkN2ZkNTA2NzY2ZDcxMDhmYWVlNDEzODQ5NjUwZjg3ZTcyMmMxYmE5Y2Y4YjYzMmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJmNGQwN2E0ZWM0NTI5YmI4YTJhZGNmOGU4ZWZmZGU3NjI3NGM4MmUyMmU0NTkyNmY2MjMxOGFiOTUxZjk2YTMifQ" + } + ] + }, + { + "id": "claim_cf8a3bf4e17c4734b05c19365c73b426", + "statement": "All CI jobs set UV_FROZEN=true to ensure reproducible builds against locked versions in uv.lock and prevent transitive dependency surprises; release pre-checks disable caching to prevent missing dependencies from being masked by cached environments.", + "evidence": [ + { + "resource": "repo://.github/workflows/_lint.yml#L31-L31", + "version": "repo-lines-v1:sha256:f3bbbe58ef06cefc721fa895cde17817da87a352714047a0a013aea771269249:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjNiYmJlNThlZjA2Y2VmYzcyMWZhODk1Y2RlMTc4MTdkYTg3YTM1MjcxNDA0N2EwYTAxM2FlYTc3MTI2OTI0OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjNiYmJlNThlZjA2Y2VmYzcyMWZhODk1Y2RlMTc4MTdkYTg3YTM1MjcxNDA0N2EwYTAxM2FlYTc3MTI2OTI0OSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDBlODMzMGExNWRiNjY4ODA2MWJkMmJmZmFlZjQ2ZjJjYzBlN2FkN2QxNjM5NDg4OTNhZGM1NTc2NmU5YmYwNCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMDU1MWE0OWM1YTVlZWY2OWVkNWIwOTkzOGNjMWRmYjk1MjNmYmU2ZjI3MWIwNjdlYTAzNGRiYzM0NGU3Y2Y4ZSJ9" + }, + { + "resource": "repo://.github/workflows/_release.yml#L409-L415", + "version": "repo-lines-v1:sha256:24040bfc54e88a0f6afc5bd2eddc942e5d77109bcfac084574f8ef8943245059:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzkzNTcyYjUzM2ZkYjQ1ZmRmZjZiNjg1ZGQzYzU5NTBhNDc3NjcyMDE1YWMxOTIwMzFmMWQ2NjllMWJlOWM5ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDhiNTNmYzAyMWU0OTFlZjhhYTlhNTUzYzE3ZmNkYTkyZjQ1NmU1MjJkZmUxNTdkNDAzNjgyZjFmMDY4ZDQ4OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYWU0Y2EwODgxYmQ4ZDI2YzIyZDRlNTUwYmIxNGNmMDVkOTEzOTI1N2Y1YWNmMTZiNGM2OTRhM2RhZDIzMTUwMyJ9" + }, + { + "resource": "repo://.github/workflows/_test.yml#L22-L23", + "version": "repo-lines-v1:sha256:a3d42222f25205004c5b413ab62b3089da07e3970084243a753c5372e1d36291:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjNiYmJlNThlZjA2Y2VmYzcyMWZhODk1Y2RlMTc4MTdkYTg3YTM1MjcxNDA0N2EwYTAxM2FlYTc3MTI2OTI0OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMTRlOGFjN2M5NDU0MmM0MDA4MmQzODJkYTA4MmJjYzIxMTRhYmIzMmViM2NjYjgwYWIzOTY3YTc4ZTI5OGYyMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiY2U2YzI4NTJiMGE5YTcxZTM3ZTZmYWE2MTRiNWYzMTI2Njk5MGNjNWMxMWMwYzE2N2ZiNjI3YzYwYWIzYzg5MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzg0NWI3ODZhMmNiZDZmNDllOWFiNmJiZWI3MjU5NTFhYzE3MWQxNDgxZjYxZTcyOWEzNjVkODc1YzkwNDIyNCJ9" + } + ] + }, + { + "id": "claim_0b7db5bd4ac549cd959364938b85eb0a", + "statement": "Release workflow blocks stable releases if any dependencies declare prerelease versions; optional allow-prereleases flag permits transitive prerelease dependencies during alpha cycles when release itself is prerelease.", + "evidence": [ + { + "resource": "repo://.github/workflows/_release.yml#L34-L41", + "version": "repo-lines-v1:sha256:75076611d40b1a5f9baf6b2eb8b8382124110b110bfdcd72282e3e723644a475:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiM2VjNWQ4NjM2YmUzNjZmMDNmYjJjZmM1NjJhYmVmNWY4NDMyMjI0ODY4MTFmZjYxZWFjZmQ2ODUxZDI2OGZhOCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNWEzOWZkZjVkY2RlYjM3ZTJiNTJjMGRkMDFmNmI5MmE0ZWQwNDJlYjk0OTJhZjhkZDVlOTRmZGRiNTMxMWIyMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTVmYmViYjVlM2ZlMzNhZTg2MDkwYTBlNjM0YzRiYjU5MGEzMzE3MzI2MWQzZjUyZmJjN2M3ODI3OTkwOGMzNiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGM5YTJmY2U2NmIyZWQ2MTE5YWNmODU0NGM5MzNhYTRlNDBiNDgyYTFhNmIyYTAzMTNkYjk4MGU5MjJiYjkwNCJ9" + }, + { + "resource": "repo://.github/workflows/_release.yml#L459-L464", + "version": "repo-lines-v1:sha256:084ee9b8f80e960e0a0038260ac1d406a94faa93dc7fb661eeb7c6cf6600a3ef:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzA5N2VlZDhmMWZhOTQ2NzZjYWViMDZkZGE2YzlmZjc2NmY4NmU2MTkxNmYzYzI0ZjQ3ZGY0NjE3NTZlYmY0ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNzhmYjZkZTUwMGVlNTg1YmZjYzg1MmNhMGEzZmYxOWE1OTg2YmE4YTUyMDZhMTQ1YmZiY2UxODEyYWVkZTYxMSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiODkxZWFlOGRhMDE0NjY2MTc5ZTMzZjdjZjc0MWYyZWRlYWUyODI5NzNjZTdlNDhlZmE1MDVkODZmODJmZWIzZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTY2OGJiNGQwN2JjMDk1OTllZWI0NGIyMDdmOGRiNTEwZjhjYzk5NzFlYmU2MWQ5YTBiNzRkMTE5ZGQxZDI0NiJ9" + } + ] + }, + { + "id": "claim_3060af4d17a8410db1993b2134916d33", + "statement": "CI change detection skips standard-tests directory in package enumeration, ignores huggingface partner as CI-unstable, and supports IGNORE_CORE_DEPENDENTS flag to control whether core changes trigger dependent package tests.", + "evidence": [ + { + "resource": "repo://.github/scripts/check_diff.py#L42-L53", + "version": "repo-lines-v1:sha256:39b10ceaaebbaea1eb3723c5cc264252f9516a3a822661b57df893a804b4cc06:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3YTRhMTcyY2EwZWE3M2I4ZjBiZjRiMTNkYmY2NzgwODY4ZGQ0OWY0OWUxMTg2ZGVkMWIzYjI1MTJjMGJlOTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5NDE2ZTA5Zjc4YmEzMTZjMDMyNTY4MjEzYjg1NWVlNzU2ZTQxNTY1ZDkxOGYwOTUxOWM4MWZkNjIwZjQwNjAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyNzhiMTRiZmE0YWQ5YWM1ZTg5Mjc3NGU5MWZiY2U1N2Q0MDFkYTc1ZWIzZWRhYmFjNjYzNGU5MTdjNTY4NWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNmNmYzOThhM2YzMjczNzdiZDM4NGE3YWZjMDRiMTRjMGI4ZGUzZTQyNTk1MDZiNmNlNzUzNWI4MGQ3YjY3MWQifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/composability.json b/openwiki/.claims/composability.json new file mode 100644 index 0000000000..6b8e595a12 --- /dev/null +++ b/openwiki/.claims/composability.json @@ -0,0 +1,168 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:2686a50367f151aef17fad64706fe0e5bb3d6d279febeecbac7d3760d2d17424", + "claims": [ + { + "id": "claim_51e65b4f126f42d986b2766ee964d991", + "statement": "The | (pipe) operator chains Runnables in sequence, creating a RunnableSequence where each step's output becomes the next step's input, and it automatically flattens nested sequences.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3075-L3208", + "version": "repo-lines-v1:sha256:50b0edb81dc9d08ad730ef192dd88e1c76aae278c6050b8381610b0fab6083ef:eyJzZWxlY3RlZExpbmVDb3VudCI6MTM0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiZWUyODI4YWI5ODNjODVkZTdkOWUxOGNlZjVhYTQzZGY0MTJiYmRiY2FiMDc3ZjdmMDEzYjUzZWQ3ZTRlNzM3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1OTA5YTUzYTYxOGQwMjhlMWQzMDAwYzBhN2FiY2VlY2JlNWZiMzdmNGU1MTFlZmU0NjY0M2ZhZDZlNjY4NzJmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJjYmVjNzljZDAzZDA2YmE2ZmU3MDBlMDhiZDA0ZjFmMDA0ZDFkZTg0ODMzMzMwN2U2ZGE5YTJlZjcyZTZmODZmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1OWRjNDBjZWRhOTM4MzYwNzU2MzZkMTY5ZGIyZDVlMDYwMTg4MTNiZWI5MmRhYjlkNzEwOWI0MjE0M2NkYWRkIn0" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L648-L673", + "version": "repo-lines-v1:sha256:b5be17ef178ed117d371c78e9412265ee7efa288aae872613ebcdd3a241b87cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiNjNkN2Y2Y2EzZTVjZWQ5MDg2M2YwMmJhYmQyZDkwOGNiY2ExOTViZTQwNTE0M2UyNmViODA3Y2E0MTRjMzYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIxOWY0MzJmNjZjNzA5MTFjMDcyMzg1YzMxMTA1NDkwM2U1ZWQxN2NmNGYxY2FjMjg1OTUzYjI5M2M1MWRjOTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkyMzEyOTc4NTcyNzU3NzcxMWUxOTFiNmYyZTlkMzA5NjdjMmNhN2JiM2U2Yzg3NGY5YjY4NjU4ODI4MDcyY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJmZjllY2NjYWJjYjk2ZGM1YzRkZjIzYzEzMzEwZjI0NjhkZTU2MjBlMDFjYmUwNTUxMDBlNjVjYzlmYjQyZDQifQ" + } + ] + }, + { + "id": "claim_1309275bbbb74672831f5010f0f69017", + "statement": "RunnableSequence preserves streaming properties: if all steps implement transform(), streaming passes through the entire pipeline; if any step blocks, streaming resumes after that step completes.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3094-L3110", + "version": "repo-lines-v1:sha256:2ef34a9d0b35166b35f7620c01895fde1aa0f19397e235d1fe9c91a230c0a599:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE1MWJhN2ExNDdlMTk4ZmQ5YTJkNjM1NGIxMjM3ZjBlYWVhNGZhMDFiMmRjN2ZjMzdiYmY0NDVjMGYzZjZkMjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM4MjhhYThlZTExY2U5MDA1NGE3YWUwMjgzYzZjNWNiMDBiNzI2YWNlYzAwMTczNmY0OTY4YzQ2NjgxMWNhY2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI3NDQzNzAzNTIyZWMzMWY0YjBlZmZjNmQxZGUwN2E5YjQ1MzFjYjIzZDlhZDNhMGNiNTY1YWIzNGY0MWMyNDgifQ" + } + ] + }, + { + "id": "claim_49e7047fbd9c48d694f4b01fdf3a563e", + "statement": "Dict literals within a sequence create a RunnableParallel that invokes multiple Runnables concurrently on the same input, returning a dict of their outputs.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3865-L3875", + "version": "repo-lines-v1:sha256:357dc57f5e818449d954a4b79fbfba43677fc3b9fac73253591a9dc732873f72:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMzNjI1NjYzZmNjMjBkMWFjNDczMGVkMjExMDBjMWIyNjBiYTdjZjFjYjA4OGVmN2I3NTVhNjQ3ZGUyYWI1NmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc3MGYyZGMyNjUyOWYyOGRlNWVlOTA3OWRiMDk2OWQzZTgzZTI1NmExYjBmOWI2ZmNmZGFmMGVhOTUwMjA1ZmIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQzZTdlNmMwNGQwZTI4MDY3Njg5ZDI0NTM4NjQwYzY5MGIwYzZmNmJhNjBiMzJmNmZhNDE5ZWFjNDRhNDBiNDYifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3899-L3918", + "version": "repo-lines-v1:sha256:7abaae83d4ea741da2276a115e6cbca83a74b6d37f46469b9e02b9f75b2f6ae2:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY3ZTg0NjVjZjNmNmEyMjhhYjdjMDQ0YWQyNzU3OGM3MDVmMmFkYThhZTQ4OTJhZWE5MGJiMDc0MGY0YTkwNGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2ZWY2ZDQ3NjI3Yzk4YzFjYWFkYTM4MDMzZDQzZTEyYzE5YTA3OTNiN2E5OTNjNWUwYTdmZDdlNjRiMmNmNTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU0NmI2MDcyZDg3NGY2YzQ2MDU4YzhmNDdhODllYTA1ZDczYzFjMDAzMzJiMzM0OTRmM2YxYmY5NGY2OGI5YTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI5MzlmMzJlNTBjNDM5NzAwOTcwOTQ5MmUyYmJkNzgxMTJkM2YwYmYyNTYwOTYxYTRiZThkZTUxOWFlMTc2YWYifQ" + } + ] + }, + { + "id": "claim_f2a2e30192e84a8d943ade69bb877243", + "statement": "RunnableParallel uses atee (async) or safetee (sync) to create independent input copies for each branch, with chunks yielded as they complete using asyncio.wait(return_when=FIRST_COMPLETED).", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L4325-L4367", + "version": "repo-lines-v1:sha256:af652106c5e10847a785649a20ab3669429460fabdde32919c3af4aa413ae7c0:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3NDc2MzI4ODc3Y2ExZWJiNzM1NWZkOWMyMTAwYTUzYjQyOTM0ODJhZmU0ODA3OTQwYzAyNTkzMzA0YWY3ZjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUxODYyZTJmZWI4YmJjYmM3Y2NhNmYxMGE4NTAzMDJhNTJjY2EyM2IwOTE0NjdiMzljMDcyOGY3OGE4OTU4MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY4ZTc0ZDM4NzUxZDM5MTM3NDYzNGFiNDY0ODJiMzE5Y2FjYTk0MzE1YzE5NmUwN2I3MDQwZjgyZWNiYmUzYmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVkOTNjYjJjNGFmYjAwZTJmOTY1ZGVlNDkzMTkyYjY0YmY0YmI2ODc5MTRkYTFiYWJmYWU5YTNkYTk3MDBhNGMifQ" + } + ] + }, + { + "id": "claim_04d245f8261246fb9ddf9c83869aa732", + "statement": "The batch() and abatch() methods process multiple inputs through a pipeline in parallel: batch uses thread pools via get_executor_for_config, and abatch uses asyncio.gather with max_concurrency control.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L1066-L1100", + "version": "repo-lines-v1:sha256:eaba4671a1d31f10055b668320373a32167c2b22020509c1cbb26e1c96f88319:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM2MDQyODVkYjcwOTYxMjMwMThmYTUyNjRmNzE0ZTU2ZTdhODM4OTdlYjg5ZGNjOGIzYWZmZjhiZjFmMzRhNzciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2MjFkYjllY2I3NmRmODA5MjgyMDdmM2I2MjU4MTc1N2I5YzM0NGZjNWQ0ZDk4Y2ZmNzNjNWI4YmQyMjhhZmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhmNmEzZDRmNGIwNzcyMDVhZmIyZmIxZDFhMzc2NmZkZTdlODNlYWI4NzMwZmMwYzI5NTg2NzVkMzNlN2E0YjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc3NmYwZmJlMGE4ODMwNGU0YWVmZjZiMjhlMTc3MTM1OWJmOTQyY2MxZTI2YTYwMTM2NTY2ZTE4MjliZDA1ZjAifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L931-L979", + "version": "repo-lines-v1:sha256:162e8b332e60ac819bd13d160c07a848461973a135ccbc10cf7ca5337413b062:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQwNGJlZWJhNWU0ZDYzMWUxN2IwNTVhNjhhMGEzYTkyNzFjOTcxYjJkMzZkZDA3NDBmYjJmYjY4YzU1MGJhZWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2OGY4NmVmZjFlNGIwMmY4NDg2ZDU3N2M3ZjMxMTdkZTA2MjhiZTE5OWJjZDk0M2JkNmYxNGNkOTU4YWM1ZWUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI0NDc4YTFlMTI0YTY1MmEzNDI4YTE3YWRmM2Y3YjUwOTU5ODY3NmYxZDhkNGViNGIyYmJmNTYyOWViYjRlYzUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFjZDM1N2M4NDFjMTcyODdmM2U4MmQyODVhZTgyZDM0YzdiY2ZhM2EzNmIxYjI2MjYxODY2MWM1Nzc0ZTNkMTIifQ" + } + ] + }, + { + "id": "claim_6e91508762e349d0baab198b14ca2dfd", + "statement": "stream() and astream() emit output chunks as produced; the default implementation calls invoke/ainvoke, but components implementing transform/atransform support true streaming without waiting for full completion.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L1194-L1213", + "version": "repo-lines-v1:sha256:23cc3478740b415df53cc79cbb93700be85319defdec671ee846f03b1fd9f5ea:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkMDJmY2NjNWE1YWFkOWQyZmRmNzU2ZmM3ZmU2OTM0MGYxZDM3ODAxNzQwMTY2MzgzMjQwYzNlYTZjMWY0NGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMyMjk1OTY3MTUzNGMyYmE1YjA3NjM2NTZiMTI5YmNkN2E5ZjIxODNjYmQwMGEwMzE0OWNmZDg5YWQyNGQ3MjUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRmYjIzYTg5OGMzMjkxNWQ3OWNiYzVkOTAxYWYyOGY0YzI3MjUxZDM3Y2RiZDA5OTlkMWZlMmMxZjkzZDFlZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlkMDdlMjcxYjA2OTBlYTNlMWViNGE2MDMzYzY0NmNmZDJjNmFkODcyYTNkNTBiYzliMWY5MGM4MjRhZTUxOWEifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L1215-L1234", + "version": "repo-lines-v1:sha256:7d041aac561a3c904849deadfc7b152866d4e3d36f4dc8256583a2f7e39044b3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM0ZThjNjMxZDczZmQwMDMyMWQ2ZTJjYjI3NTBlYTYzZWFiZWU4YjM2NzZiYzBhMTNmMzBmZDU0NjQ2NTAzY2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkMzYzZGU5MmRjZGQwMDk3YjMxMDQ3N2MyOGQ4MjJhNTgzOThjNmYyNWI3MDc4YWJkNjcwNDU3OThkN2JmNDIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBkNTZhMjE3OWVmZjU0ZWM2ODczN2Y5MDQ5ZDFlN2JkOGI4NjI4MmE3ZjhjMzFmZTI2ZTQyZDRlZjE0MjhmNzAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkMDg1MDY4OTU0YmFiNzhlNTM3ZTYwMmM2YTE1OWNkOGQzMzMzNjcxY2M2ODc0YzhlYzA0N2ZiZjc4ZTAxOGUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3827-L3861", + "version": "repo-lines-v1:sha256:91507446afb094a9113b451f1950223dbc4ed2bc970240ef4f5fbc65d1f278f3:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkMDJmY2NjNWE1YWFkOWQyZmRmNzU2ZmM3ZmU2OTM0MGYxZDM3ODAxNzQwMTY2MzgzMjQwYzNlYTZjMWY0NGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRkYTJlMjU2MjkzODVhOWNhN2QzNjNlN2M3ODFjMzc5YjA1ZTE1ZGVlY2E5ODlkMDEyZDFiMzgyOWZkMmVhYWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhhZmU0YTdmZWIzYzVlN2ZiOGNjZjcwZmE3NzIzNDkxYWMzOTgwOWRmZjE4YWVmMmFiODE1YzExZjc2MzM0YTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc3MGYyZGMyNjUyOWYyOGRlNWVlOTA3OWRiMDk2OWQzZTgzZTI1NmExYjBmOWI2ZmNmZGFmMGVhOTUwMjA1ZmIifQ" + } + ] + }, + { + "id": "claim_7b5e273603c549ca9f0aa7d95ad36c84", + "statement": "RunnableBranch evaluates a list of (condition, Runnable) pairs in order and executes the first matching branch; if no condition matches, the default branch runs.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/branch.py#L43-L97", + "version": "repo-lines-v1:sha256:ca305dce47126f042d2f6f2d0ee7f208bbb7ea3ba6c8bbec19cc4834cec68017:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImExYjE3NjU1OTc3OTQwMjNjMjVkY2E5MzMwZDYwNDk1NDcyOTI3MzBjZDYwZWZlYThiNWFkOWNlMzhmZjQ0OWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZhNzM3ZTFmNDBlYzJkYzlhNjA0NGM0NWZiNjg1YTE4ZGFlYjlhODg0YzM2NzBlOThiOGYyZmY3MDcxYmQyMTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU5YzA0NTAxNWEwM2FmNzYyOTYzNGUwNmU5YmUzZjg4YjI0MjZhYjVhMzdmZTkwYjI4MmJlOGY3ZjFjZjc3ZGYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ2ZDQxNGE4OGE2ZTgwYTU3Y2U5YTNkZjBlZThhOGQzZDcwZDUyZjA3YjIyOGE3OWUyYjk3Y2E0Y2VmMjA0MDUifQ" + } + ] + }, + { + "id": "claim_d343eeb0e7aa4ee29ffd49b514f4e85c", + "statement": "RouterRunnable routes to one of several Runnables based on a string key in the input dict, selecting which branch executes based on the 'key' field.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/router.py#L46-L117", + "version": "repo-lines-v1:sha256:c9756af590747da1fe8c53b6a9b264e86ce3e7126e99a5f38038b8edff1d2575:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjliMWQyMmIwZjU1Njc3N2Y2ZDlmNWIyZmY1MmIwOTIzZWVlYzVmNDhmNjFiNmUwZmRhYzgzMmQ3MDQwYzNiNjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjOTA5MDk4ZjU3NTNhMGI1NGVmMzFjOTU5NjM5Yzg1N2E4MDdkYTM4ZDZlNjgxNDg1MTYyZWU3YTNiMDQzNDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk1YjczN2QzODU0ZjgzOTNjZWIyYzcxNzIyMTI3NDI2NzQyNWY5ZWI0NjJkMTc3N2VmMjhjZDZhNjQzYWQ3ZjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU3ZGNlNTdlMDdiMTM0MmRhY2YxNmNiZjNjMTllMzUxMzFmNWM0YTg3MGU2M2EyMGY3MGI2Y2ZkYjVkMjFhYzQifQ" + } + ] + }, + { + "id": "claim_0362cf9d984f4c15aaf06797272954a1", + "statement": "RunnableSequence.invoke() executes each step in order, passing output to the next input, and wraps each step as a child run in the callback system.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3430-L3463", + "version": "repo-lines-v1:sha256:976984f151eb788f97f2d92fd5172e81c49b088670c5a2545910700f61586678:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlNGExMDE2MjNjYjY2YmFjYzAwNDk1N2NhMzI2M2E2YzY5NDU0NTk2OWZlMDZiZDI2MGIzMzAyYjNkYWRhMDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQzZGFiMWFjOWUzODkyMDYzMDc1ODgwOWI5ZWQwZWE5NGJiYjFiMDQwZDI4MDNlMDg0MjdlODUwOTE0ZGRjODYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhhZmU0YTdmZWIzYzVlN2ZiOGNjZjcwZmE3NzIzNDkxYWMzOTgwOWRmZjE4YWVmMmFiODE1YzExZjc2MzM0YTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU3ZGNlNTdlMDdiMTM0MmRhY2YxNmNiZjNjMTllMzUxMzFmNWM0YTg3MGU2M2EyMGY3MGI2Y2ZkYjVkMjFhYzQifQ" + } + ] + }, + { + "id": "claim_d5179fe7d3f34d84be92c748c6af6304", + "statement": "RunnableSequence.batch() calls batch on each step in order, feeding the batched outputs of one step as inputs to the next, enabling efficient parallel processing through the pipeline.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3505-L3530", + "version": "repo-lines-v1:sha256:2129e2cca7d851d1ed09c0b6d1d4088e490e3a7833fe1ad08c20af6212066c4a:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJhYWU4YTBjYTc2Zjk4ZjdiN2RjNzk4NDk1MWQ2M2U1N2RiM2M2NDU1NzljYTUxYmFkNmE1MzlkNjE0ZWRiYmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjViMDA1MjRmYTNkZjM0MjNmYmQwMTIzYzY5YjcyZjZlYjY2MmYxZTZmMmRiYmM0YjgxMzUxMTAzMDgxOTA3NTciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYzZTgzMDFlNDA0YjVmZGMwZWNmYjg1OTkyYTk0YjgwMWFkNzA5ZjZlM2U3MzBjZjdiNDkwYWQzZDcyNzJkY2EifQ" + } + ] + }, + { + "id": "claim_6755e7507b7a49e99ecd66cc7bccd61b", + "statement": "Every Runnable method has an async counterpart: invoke/ainvoke, batch/abatch, stream/astream, transform/atransform, and batch_as_completed/abatch_as_completed.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L1001-L1064", + "version": "repo-lines-v1:sha256:a3f7d87ad9b0597d3d824f02452ca9c52994ad2156a1dfb9c36247784c2cf267:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFmYzhkNjQzYWQwMGUyYjJlNmQ0ZTlmZWI3ODk2OTY0NTc5NzA5N2JlMTRiZGZjZWQzODU0YmQ0NTkzZWM0ODAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlhMWY3MjQ2NWY4ZTJmMjg4ZjhlY2E5Nzc0YzYyODc2NmUxOWQwZjcwNGQyYWQ1NGUxMDhiZTNhMWMxNzZkM2EiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI1NzkzMGNmZDVhYjBjNzM4YzZkM2YxOTlkOTdlZGNiZTE4NGQzMmRiN2E1YTA2Yjk0ZWJkMjllNjI2YmRlNGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM4NjE0OTQzZmYwMmFiZWVkMWM4YTk2ZmVmNjRkZTY3ZDllZjY3NzY1NjA3ZDg3M2MxMTk5YzA3YzZlMGMxYzIifQ" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L886-L929", + "version": "repo-lines-v1:sha256:9aa7c2781e131a44b9092e54c3436f4309ea52538c7e74d52f5169bcefea15f3:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlNGExMDE2MjNjYjY2YmFjYzAwNDk1N2NhMzI2M2E2YzY5NDU0NTk2OWZlMDZiZDI2MGIzMzAyYjNkYWRhMDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI3OThkYmMxMzk2MDZkNTA1MGI3ZDI1YWRiY2NlY2Q5YzQyOGM3ZWE2ZTQ2ZmVjZGYwYjI3ZmE1NjMyYWI3NTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkzNmRjMjllOGE1ZTYxMTYyZTJkY2YxZTRiY2FiNDc4OTg0MzMxZmMzMWU0YmExNTY5MDVlZTMxNThlM2RmZTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwYTU5NTE2MjU1ZDg3ZWViNGM4NDhjNWY3ZDY4MjQyODZlY2MyMWUwYzAyMzRjOGI3ZWM3ZjE1YjY5YzRhZmYifQ" + } + ] + }, + { + "id": "claim_9b1e02a8d6e0423c9fb2574f36b64c65", + "statement": "Composed chains infer input and output types from their components via input_schema and output_schema properties, enabling validation without explicit type annotations.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L157-L159", + "version": "repo-lines-v1:sha256:f592ce53680fb6597b6d31b1f0b0d5cd9ae6ebc63d6db571ec39134a843881c9:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNGZmMzI4NTNlNzkzN2FmN2FjODEwMWRlNDk3MTU1NGJlM2ZjNGVlNTI1OTBlZDUyZjNjOTBlYTlhMjEzMjIyMCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNGY2MzQzMjRhZGIwZTQyNDdlMjcxZDZiOTU5ODJlNTA5N2Q4YjM1ZjQyN2JjZDljMWU0ZGNkYTU5NWY2MDc1MiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjEzMDQxZTZhMzBjZWJlNTQxMDcyMTk2YmU3ZGIyOGUzOGJmNzc3NDk4YjZhMzNmOTBjYmM3Yjg2YWM4M2Q1MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGJmZmIyODQ1MTY4ZmU1MTczYjM1MTc3NmQwZGNmOTcxMzgyYjg3NzYxMWMxMzJkMzVmNTU2YmFhYzY2NWE1YyJ9" + } + ] + }, + { + "id": "claim_49de73792d784622bba1b2f4f6b4ec88", + "statement": "RunnableSequence and RunnableParallel are automatically serializable and support LangChain's tracing and callback system for debugging and persistence.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3210-L3218", + "version": "repo-lines-v1:sha256:8af5f7a2d4e8fda2e9a8b47e2fd3e74540279cae2b8fa87cdb906273a826ae81:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODIwYjQ1M2Y1ZTBjOTJkNWY2YzI1MWE1ZWExOTYwZDcyZjNiZDA3OTU2YWU4NTdkYTIwZTMxYTg4YzU1OTY5ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNmY0NmQ3NDk4YTFhNmJiNDkwMmUzOGQyNzBkMjRiYWY2NzM4NzAxOGY3MzVhZTI2ZTVlMjRiYTQ5MmUyYzM4NCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiY2ZlMWI1ZGFjMDE3YjE1NzEyNjQ1NWRlN2MzY2EzODU2OTE3NmRiOGZlYjRlMGU4ZDM0ZGMzZmY5YmMzNzU3NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiY2YyMWM0ZjQyMzMwMDZlMjViYWE1NjEzZDgwNDgzMWJkNzJlZGViY2U4YWVjYmQ5OTU3ODkxYzBmN2YyZmI5OCJ9" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3976-L3990", + "version": "repo-lines-v1:sha256:dcb170b588e742a6b114ec378abf0fe9adc59525cecfedf62848d5e95203bb96:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgyMGI0NTNmNWUwYzkyZDVmNmMyNTFhNWVhMTk2MGQ3MmYzYmQwNzk1NmFlODU3ZGEyMGUzMWE4OGM1NTk2OWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmNDZkNzQ5OGExYTZiYjQ5MDJlMzhkMjcwZDI0YmFmNjczODcwMThmNzM1YWUyNmU1ZTI0YmE0OTJlMmMzODQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAzMjBjM2ZhOTJjODlmZTA3YjYzZjI3Y2Y2MzY2ZmNiNjM3YTZmNGI5M2E3ODAxMTIwZTU0NGQ5MzVkNGMwZDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUwMWJiYmQwMjVkMTZmMjE0MzM1NWI0NGQ3MzQ1OWQ3MWQxODM5OTJlOGI3NTE2NWMxOTAxZjMxMTg0ZWYxODEifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/dev-commands.json b/openwiki/.claims/dev-commands.json new file mode 100644 index 0000000000..70ac36df65 --- /dev/null +++ b/openwiki/.claims/dev-commands.json @@ -0,0 +1,182 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:7dd5ad8767d8a3f658ed06a962ccaafe416623877d4864ce71de856a4ae7b52f", + "claims": [ + { + "id": "claim_42b2b3c1ac6e4a938efa7b2651c79b81", + "statement": "The LangChain monorepo uses uv for dependency management, with each package in libs/ maintaining its own pyproject.toml and uv.lock file. Dependencies are installed via uv sync, with optional group selectors (--group test, --group lint, --all-groups) to control which tools and test dependencies are available.", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L43-L63", + "version": "repo-lines-v1:sha256:18c23a068133429888d5554c52abc7fad7af4e7cfc99ede025695ab2769177cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBlOGYyN2U5YmQ4NzAwYmMxMzlhNWEwOGFjMWRlZTY1MTZjYTE4NDBhMjA1YTZmN2U3MzJhZmVhNzE1ZTk2MTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3YzA2YzU2MWQ1YzMyNzM0MzY4NTI2OGE0NmIzMzkxZjM5OThlZWU4OTBkMmJlNTFmMGQyMWNjNGE1M2FlNmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE0ZTBkNjliZDQ4MjFmNTY2NDVmMjAyZTMxMDA5OWI4NmM0ZTVlYjkyOWMzOTQzNDE1M2U1Yjc2Y2M5NjY5MzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ4YWEwOTVmYTY5YmU0NmRmODUyZGRhMjE2NDcxMzFmMmIwMmMyYjIyZTIzOWY4YzdmZGUwZjJkZWJmZmM3OTcifQ" + }, + { + "resource": "repo://libs/Makefile#L6-L20", + "version": "repo-lines-v1:sha256:52af6517b5eee3008d1289f1a6d8c647945a2522bbbd3d6626657db93a1e6923:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM2NmM1MTM4ZTA3ODA4NDMzYjY2MGE0MjdhZmVhN2FlYmFiZmM3MGM4MjEyMWY1YzQ0NmExNDg5Njc3ODkxZjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4OWEwZmIyMTcxZjliNTk0YmQyZjA5YzQwMmEwYjU1MmRiNjNiNTBlZDBhNzE1OGEzMWQzMGFlYWRhYjVjMzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmZDBjZTQ2Yjk2NTUxMDYzMDU0NzE0MmJkNTc2MWE1ZGIxOTFhZjE5YzNlZDI3ZWU4N2M4ZmQ3OTBiMDgyM2YiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/core/pyproject.toml#L49-L80", + "version": "repo-lines-v1:sha256:8a202a04808f1e681fe85e6c66f6d916275baee9e0eea5d485e754e89227e0b8:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiYjc0OTliMWNlMWM0ZjRiMjUyN2YzYTMyODRjYWM2YjZhMWUzYmY0OTg4YjU2Nzk4MmMzZGM4ZTlhMTdmMjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU1NTkwMTRiYzkzMDk3ZTg3MWNiYjg0ZWUzZTYxNWZkMjExMDM2MDc4NjA0MjQ3NjAzZTQ5ZWJhZGM1ZWRhNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA1MDVjOTY3ZWE1MDEyYWZmNTM0NjFmNzQ3ZDJlMzJhNjViOTQxN2MwNmEzMzc1YWRlYWMzY2FkOWI0N2NhZmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZiNzAwYjhhOThkMTg2MTZmZTc3NjU0MWYwNzI1YWIwMzM2YWFlOWFhZGQ3MmRhNWFkNWE4NjcwYTk3MzJkMGYifQ" + } + ] + }, + { + "id": "claim_96ea49f4bd7c49bd8e8790338ecd76ed", + "statement": "All packages in libs/ expose consistent make targets: test/tests (run unit tests with pytest), lint (ruff check + format --diff + mypy), format (ruff format + ruff check --fix), type (mypy type checking), and help. Package-specific targets include integration_tests, test_watch, coverage, and extended_tests.", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L13-L89", + "version": "repo-lines-v1:sha256:9c4d013e2ba6c7548f84c3cbb660024da3655fc5ef32aaf08f6c48d54419c57f:eyJzZWxlY3RlZExpbmVDb3VudCI6NzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA5YjMwY2U5M2Y4ZDJlZTZkNjQ3ZWIzYTdkYWI0NDY0YjVkOWI3OWRhMTkyOGRhNjA2ODA0NzNkMmNmNGU2ODEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkNWE3ZGNmNzEwMDEyOGZkNzMzY2ViYmRiNTIyYWJlNzQxZmFiMGQwMTVjOTU1MWJiOTMzYWEwNjIxNzNkMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI0NjNkZTM4NmUwNzkxYWRkNTJlM2Y2ZTNhMDlmMmJmNjQ4ZmU0MzEzMThlMTkxNWI3ZTVkZTk5ZjdhNTg0NTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/langchain_v1/Makefile#L40-L129", + "version": "repo-lines-v1:sha256:f643d0b9181f14cfc810d519edb0af3493517a564f6dea277402be3b3de7f619:eyJzZWxlY3RlZExpbmVDb3VudCI6OTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQwMDdiNTExODg0NTFkYjM3OTNhZTI0ZWExNzk0ODU4MzY3MmVjNTUxNDJhZmFiOTVmMzE0MTNkZjExZDViOWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI4OGMwNmFmMTZiOWQ3MTY4MzZlNDllNzc4YjEyZmRjOGQ0MDEwNmNjZjFjNDY0N2MyNzBlZDVhZjE3ZWY1YWUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgwNmI0NDhlM2NkMDdkMDZkNTU2MzZmNDAwNTkzMDA5MTUyZTc0NmU4OGRmMmMxZGIzM2VlM2ZiNWQ4MjZjODkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/partners/anthropic/Makefile#L16-L81", + "version": "repo-lines-v1:sha256:ed90c8e544be1489b747d90dfa9a989002b052cef1c13e1a3da7df0a296e8ab5:eyJzZWxlY3RlZExpbmVDb3VudCI6NjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA5YjMwY2U5M2Y4ZDJlZTZkNjQ3ZWIzYTdkYWI0NDY0YjVkOWI3OWRhMTkyOGRhNjA2ODA0NzNkMmNmNGU2ODEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQwNzA4MzQ0YWUyMTVmMzUxNjBmZjEzNzUxZjM2Y2IwZGRhMmE5ODVmM2Y1NzFjY2E1ODQ5ZDhmMjRkZWRkYjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU3M2Q4ZjU2N2U5NGRiNTVjNTY0MTIxZmU5MzM4MmY4YTc3NjcxYmRlYTZhZGZiMmQ4NzU2N2NlOWU2ZGJlZDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_347794b754514e73bc35701f1c522cc7", + "statement": "The .pre-commit-config.yaml defines hooks for YAML/TOML validation, file ending/whitespace normalization, per-package format and lint execution via make, and version consistency checks across langchain-core, langchain-v1, and partner packages. Hooks prevent commits that fail linting or have formatting issues.", + "evidence": [ + { + "resource": "repo://.pre-commit-config.yaml#L1-L100", + "version": "repo-lines-v1:sha256:a9b9a140da0a9184682b104e2e262dcba390cc849f401fa81833bd3ceeafd102:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI1YTExMGRiNWE5MzU3Y2RhMWQ3ODIxNmNkYzBiZjhiMjU1ZTMzOGNiYzFmMzI3ZTBkZjA4Y2ZjNzczOTFkZGVmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiMzIxNDFiMWFlZTU0NWU4YmI5MWY5NTE5M2MxOTA0YzgxZDgwY2Q5MDk5NGM2NTM4ZmUxOGIxNmFlNzg3OTg3IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI4NTRmY2ZmNDA3YmE0ZjU4OWIyNjI1NTU5OWZlMTM0MWNlMmRmYTc4NGNhYTY2YWU3ZDlmNWM1YmQwMDdlM2MyIn0" + } + ] + }, + { + "id": "claim_b27ba6d975ef4dd9b451d4443dd72b46", + "statement": "Unit tests reside in tests/unit_tests/ and run with socket restrictions (--disable-socket --allow-unix-socket) to prevent accidental network calls. Integration tests in tests/integration_tests/ are run separately with full network access via make integration_tests.", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L199-L205", + "version": "repo-lines-v1:sha256:796f55ce45583a14e8b50842b84a1c8a2f7583da169c91d9c35b00647d9eedea:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMGZjNzE3MTYzNzQ5NjY3Y2JlZjJlNTY4OGRkMGIwM2YzNGY5NzhlYjdhZWM2ZTFiN2M4NTM2MjNjMmMwNmExMiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNjk1ZjAxY2Q1ODhjZGIxMTlkYjhlNjcxNjMyZjRhOWU2ZTVlN2M5MGEzYTcyOTRhNzRmNDU1ODg0ZTg5YmRmNyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOTg4ZDBjN2YwMDY1ODgwZWY3MDY1MmM5N2QxOWU1MzQ2NDUxMTllNTVmOWMwYTMxZmI1OTMxNGZmYzQ1N2MxZSJ9" + }, + { + "resource": "repo://libs/core/Makefile#L13-L20", + "version": "repo-lines-v1:sha256:ab8103f9ee7c85d9fb1c9d3d0cbe6264d1384aac292f90c74f84c77c06ed5398:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDliMzBjZTkzZjhkMmVlNmQ2NDdlYjNhN2RhYjQ0NjRiNWQ5Yjc5ZGExOTI4ZGE2MDY4MDQ3M2QyY2Y0ZTY4MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTdmYmQ4MDQxMWFjZWNkZDkxOGI4ODk4NDE3MTg3MGY5NWU4ZjE3YTZiNDA0YmU4MDY0YmE3NmJhNjc2NDk3NyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjQ2M2RlMzg2ZTA3OTFhZGQ1MmUzZjZlM2EwOWYyYmY2NDhmZTQzMTMxOGUxOTE1YjdlNWRlOTlmN2E1ODQ1MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzgzNzQ1ZTU1YTIzYmI0MzZjYTUxMGFiMTgwNGQyODMwMjhhNTJlNzc5MjE5YmNlNzAyODRkOWY3YjEwMDcxMCJ9" + }, + { + "resource": "repo://libs/langchain_v1/Makefile#L40-L44", + "version": "repo-lines-v1:sha256:ae657b84ebf74f18cd8f352372e385423674748f29988c98ec68800c9db77695:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDAwN2I1MTE4ODQ1MWRiMzc5M2FlMjRlYTE3OTQ4NTgzNjcyZWM1NTE0MmFmYWI5NWYzMTQxM2RmMTFkNWI5ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjJlM2NkYTc4MTUxMzYwMTUyYjhhYzVlYTBhN2Q2ZTU1YmQ3NDFiMzVhZTVhYTdkZWMzNTkzZmY5OWY3Njg3ZCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiODA2YjQ0OGUzY2QwN2QwNmQ1NTYzNmY0MDA1OTMwMDkxNTJlNzQ2ZTg4ZGYyYzFkYjMzZWUzZmI1ZDgyNmM4OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZWNhOTJmYWZiNTkyOGNkODI5NTNkZTk5M2QyMGM1MjFkYzFiN2Y2OGY3YTVlMTZlZGI4YWVmYWJkYmEwZjAwYiJ9" + } + ] + }, + { + "id": "claim_959d853bab90424c9967eb7be91e8a1e", + "statement": "Ruff is the primary linting and formatting tool. make lint runs ruff check (linter), ruff format --diff (format checking), and mypy (type checking). make format applies ruff format and ruff check --fix to auto-fix issues. Ruff checks and formats Python files and Jupyter notebooks.", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L74-L82", + "version": "repo-lines-v1:sha256:b82a369b901d62f1b1145bc7b798f34b59789353970045ac54779e7192c3a67e:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOGU0YjVhMWE1NGExNjUwNTViMWUyODYzMGJlZWM5ZWIxOTM3ZDM3MDkxZjYxNjJmNDM1MzU5MDg3NmUzOTZlOSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDdjMDZjNTYxZDVjMzI3MzQzNjg1MjY4YTQ2YjMzOTFmMzk5OGVlZTg5MGQyYmU1MWYwZDIxY2M0YTUzYWU2ZCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWViNmQzY2I2YzNhY2FhZDAxYjI0MDRkYWNkZDA0NGJhMWMzYjU3MGE0ODA1ODRkYWQzMDVlYmI0MTRiNTkzNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYjMxYWViMDIxYTA2MjZlNDBhZWQ3YWQ3NjE4NjJiYzdiOWRkMjI2NjBhOWFlYTg1YTA2YmM5NDZiYWFmZDUzMiJ9" + }, + { + "resource": "repo://libs/core/Makefile#L60-L72", + "version": "repo-lines-v1:sha256:3bcc88867c78ef459094b78a7574f528408682409c87c2eddfdd14836473cc96:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkMmM3Mzg1MjgzZGQ4ZTg0MzY2YzA5OTFlOTgyNjVlMmVlNTJmY2NlYWU3YTAyZjgxOWMyODcwNGY0NzBiYmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVhOWM1NmY4YjEyM2I3YmY0NGY4MTI1ODEwODZhOGRlNWIwY2E5NjVmYzA2MGIwMmUwNTFhZTllOTQzMDg2NTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlNzJiZWQxNzkxNWM0N2VkMzk2NTE4ZDRlZmIwNTQyYzJkMWFhOWUyZDVhYzFlYTZiYjdjYTg1N2I0OWE5ZjgifQ" + } + ] + }, + { + "id": "claim_bee3b5db028f40f98c40b890a6ba5d6a", + "statement": "Mypy provides static type checking via the make type target. The make lint target includes mypy checking. Type hints are required on all public functions and return types according to code quality standards.", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L175-L192", + "version": "repo-lines-v1:sha256:640ff52e1d50406006089cd00dc65c5d4a080be0ef2cd5d06025102cd8d709dc:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA5OWQ1MjI2Y2I0MGM4OGIwNzY5ZjRlZjQ1Yjg0Mjk3NDJkZjY4YTIzMTkyYjhkMmQ3N2QxNjhjMDgxZWEwYmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3YzA2YzU2MWQ1YzMyNzM0MzY4NTI2OGE0NmIzMzkxZjM5OThlZWU4OTBkMmJlNTFmMGQyMWNjNGE1M2FlNmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ4YTg2ZTkyZWY5ZGMxNDM5ZTliZGY3ODJmNzYyOGQ0Y2VkN2JiZGQwZjBjMTMxYzFkZTc1NjFlMDQzYmNhNjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyODA3ZjgxYjMwMTViMGZkNDhhMWE1NDQyY2E0Y2FjNjRlMTc5ZTBjMDc3ODNjZDZkYmZmYzZjMjNhMDE3OWIifQ" + }, + { + "resource": "repo://libs/core/Makefile#L56-L67", + "version": "repo-lines-v1:sha256:40be48526eaf664b0b35ad81d7cecd228d8b29f2c712838adfdcc779fa8f29ca:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIwOGVkODc5MzFhYmQ1YzNjYWFlZTY0YTk4OTA4Y2I1YmJmNzc3MTgzYWFkYTY5M2Q1ZTFkMGRiMDllNTU4OTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyNjRlMTcxMjBkMTA1NzYzYmE5M2I5MmFhMDdjN2M0ZmIxOTMzNDczMzFmZTVjMDQyOTJjOTA5ODkxOTVmZjUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFlOTc2MGIxYzg3YjA5OGM2ZGUwN2ZhYWFmOGEzZDc3ZTJjNjBlM2MyZThmYTJhZDFjYTZmYmZhNGJiNzJjZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVlNjQyYmE2MDk2NjkwODAzMzk2N2RhNjk1M2Q3ZWM5MDMzN2M5MDA2ZjViYmRlYzEyMGZmYmUzN2U4OTdjYmEifQ" + } + ] + }, + { + "id": "claim_c0f5ca806f244a4498fd5648746de4b6", + "statement": "Each package's uv.lock pins exact dependency versions for reproducible builds. Lock files are regenerated via uv lock and verified with uv lock --check. The libs/ Makefile provides make lock (regenerate all) and make check-lock (verify all are up-to-date). UV_FROZEN=true in Makefiles prevents accidental lock changes during uv sync.", + "evidence": [ + { + "resource": "repo://libs/Makefile#L1-L20", + "version": "repo-lines-v1:sha256:b703a85cdfdb60e98c41ec8261d9b78f0e7137105ee9da36b8cb919b2d35858b:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQyOGQwZmRiYTcwNTRmMzNiZTZkYTQ3MjU4OTFjMDA0OTIwMGZkNDNmNTAyMWY5N2Y2MmI4N2U4ZmNjMjBhZTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4OWEwZmIyMTcxZjliNTk0YmQyZjA5YzQwMmEwYjU1MmRiNjNiNTBlZDBhNzE1OGEzMWQzMGFlYWRhYjVjMzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/core/Makefile#L10-L11", + "version": "repo-lines-v1:sha256:3227bd380fca6b422c3023518bfd0c5068ed6ab7e74ccd5806700d0fee0b7be5:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZWJjOGYyOTYzMWRjYmM2OGFlNjcwYTc2MWNlNmQzMzM2MjQ5NzFjYzQwMjZmOTY0ODliZDViMWMyMTQ3NjhiYiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZGJmN2M4YzA4ZDAxZGRlYzllOTUxYTcwNTU4Y2U2MmRlYzFlY2ZjNmU0OTZkMWM2MzYzMTQ5YjQyYTY5MWRmYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYmVhNGNmOTJhZGZhNWNjN2U2Nzk0MjM1MmNlM2U5YTFiZjljMzJiODBhNDQ5ZTVlZWYyNWQ0NGIwZjdjODliYiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYmRhY2ZkNWNkNWFhNzhjNGVhMDE0YmM0MTVjMDE3ZGVmMDQ1NjdlZmM5NzM4ZmMyNjFkNDM1M2U4NjE5YmRkZSJ9" + } + ] + }, + { + "id": "claim_ee2569a5562144e7a249a2971381dba3", + "statement": "The make test_watch target runs tests in watch mode using pytest-watcher (ptw command), automatically re-running on code changes and auto-updating snapshots. This enables rapid feedback during development.", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L22-L29", + "version": "repo-lines-v1:sha256:97fe2ee064ef9ea5bf5beb1035f1d8098e37c23d4c3b14681a513a622dda2c61:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTdmYjk1NDFmNWIzOTA2OTIzNmU0YTFjNDY5YWUwM2M4NDAwNDE5MzBhYTlkOGQ4N2VmYTVlNGNlODc2YTQ3NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDA5ZjJkNTg2OGUwNWFmMjM1OWJjYTY2ZjNjMjBjYzY4ODIyYWUxNTQ0NWFmODMxYTBmYjRhYjY2ZGQxZTdiYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiODZjMzMxNWVlMmNkY2IyNmQzZmMwMWNiODk2ZDc2ZWY3YjBkMGM4OTI0N2JkYTRmYWU2MmRhMzFmNTQwNDI1MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMjQ4MmU4ZjYwZTI4OWY1Zjg5MmRjYzM5YTY1M2ZhMDNmMTQ3NjE4YjEzZjAyNTYzNTA0Zjk4MTIwYzllNzk2NCJ9" + }, + { + "resource": "repo://libs/langchain_v1/Makefile#L58-L62", + "version": "repo-lines-v1:sha256:f82228406f037bdfd11271df7c7ec6a08e0225b5a7509a88ec33ec74b8167344:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTdmYjk1NDFmNWIzOTA2OTIzNmU0YTFjNDY5YWUwM2M4NDAwNDE5MzBhYTlkOGQ4N2VmYTVlNGNlODc2YTQ3NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjJlM2NkYTc4MTUxMzYwMTUyYjhhYzVlYTBhN2Q2ZTU1YmQ3NDFiMzVhZTVhYTdkZWMzNTkzZmY5OWY3Njg3ZCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiN2JhZDU0ZDIxNzk2ZDAyNjZiM2U0YTRkODAwMDM5YTQ1ZGYxNjVkZTAwNWU1MjQ1NmZiMzA3MzBlMjcyNzVjNyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDU4Njk3Y2E4OWZjMDQ5MmNkMzExM2Y4YmQ1ZjUwMGM2NDI4YzI1ZDc1MDcwNTYwNmM3N2Y0NWI1YjljZGIzNyJ9" + } + ] + }, + { + "id": "claim_c321ef01edbe4c9ba1d1bbda30da51cb", + "statement": "Targeted testing is supported via uv run --group test pytest for any test file, and the make test target accepts TEST_FILE parameter to run tests in a specific file or directory (default: tests/unit_tests/).", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L65-L72", + "version": "repo-lines-v1:sha256:31a4bfdbc86236cc94b3c911a3021bae7225923c76578064979e5940b39e80e2:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNWRlMmY1MzI4ZWU2ZTgwNTBlOGFhZmQ4ZGM4NjlhMDY5Y2Q5NTg0ZWRhYWY1NGZlNTg5ZDczYzNiN2RjZDc5OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTEzYTY5MWJiMDQ5NzI2YTFiMjExODRmZWUxZWRiZDI1ODcyYjY5NjUyNjJiYzVmMmRjYzU0ZTMyNDY5NjMxMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTY5YzI1Mzg5YzNjNmNiNmRlZmM3ZDU5YmJhZmZjOGZmOWY2OGMwZjkxNTc5NjhmYjdiNzA1OGNhYTk2Y2UyMSJ9" + }, + { + "resource": "repo://libs/core/Makefile#L7-L20", + "version": "repo-lines-v1:sha256:e9c3c9ecc6833c74c505273c5dd890e9237e0e16de1afb11e37c794607712579:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU5MjUxMjg5NzRmZWQwNDg1MGJmOTM4MjBiMDQwZDI4ZDdkZDRiMGMwODJkZTMyYjk1YjYzYjkzN2I1ZTk5MjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE3ZmJkODA0MTFhY2VjZGQ5MThiODg5ODQxNzE4NzBmOTVlOGYxN2E2YjQwNGJlODA2NGJhNzZiYTY3NjQ5NzciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU1NTQ5MzgwNDA2MjFkNDNiNThlZTU1NGUwZjAxYjNkY2JlY2UwNDdhMzRlNDYzZGVlY2NjYWM3MTM4ZGM2MTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc4Mzc0NWU1NWEyM2JiNDM2Y2E1MTBhYjE4MDRkMjgzMDI4YTUyZTc3OTIxOWJjZTcwMjg0ZDlmN2IxMDA3MTAifQ" + } + ] + }, + { + "id": "claim_83c0f0ccd48843778e8e916dd8acce9a", + "statement": "The make coverage target runs unit tests and generates coverage reports in xml and term-missing:skip-covered formats. Some packages (like langchain_v1) also provide make coverage_agents for middleware and agent-specific coverage.", + "evidence": [ + { + "resource": "repo://libs/langchain/Makefile#L18-L23", + "version": "repo-lines-v1:sha256:27bba5ffb44d7f4ff7ae2536af023b678440431b418b43a547a6dfb1185a5219:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZWJkN2E1ZDU2MDIxYTA3YzI5Yzg2ODlmMzEwOTc4MWQ5Y2ExZThlZTNiYzM4Njg1MDVhYzY0YTE0ZWI4NDQ1YiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOGMxZTk2NTdjYTQ4YmE3NmQzMTg1M2VmNGZmMmJlMWQzNGQzNzVjMWJlNmQ2YWIzMWJiM2MwM2M0YzU3NjUzMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYmE5NWIwMjk4NGYyMWJjZmY4ZmRhZDFlOWYyMDM3NTRlYzE4YWM2ZmI3NmZiYTNjMGVmYTExNjI5NjRhMjBlZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNWFlYmEwZTgxZTgwN2Q1NWM2MWEyYTk3YzY3MjZmNWI1ODhkMDdmN2Q0MWVjMjA5M2E4OGMzNTdlNTQ5YmZjMyJ9" + }, + { + "resource": "repo://libs/langchain_v1/Makefile#L23-L38", + "version": "repo-lines-v1:sha256:1b5c1a536f8eee0f5d35d21c6dc1d0cfb8ba61a34bde7d1c485b6f94840f80b0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzODQyYzFlN2U0NmFmMzA5YWU3MmQ5ZmRiMzM5YzdiZGYxY2U4OTYxNGQ1NDRkYWE5ZDVlNjlkNmFmMTE3YWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjljMDQzMmVhYjY2MzJmYzEzYzkxY2E4ZDllYmY3NmY5ODMyOWFjOTY4MjVkMmNjYjk5ZDRhYjljMDg2YjViODciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI0NjNkZTM4NmUwNzkxYWRkNTJlM2Y2ZTNhMDlmMmJmNjQ4ZmU0MzEzMThlMTkxNWI3ZTVkZTk5ZjdhNTg0NTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIwMjIzMjZhZTBhYTdlODJkZDY5NjQyMTI5MjIwY2RmYTEwY2ZkMDg2Y2RmODNhYTIxMzRiMDlkZGJjMjhhMmEifQ" + } + ] + }, + { + "id": "claim_00c99ac0872a43b3a01b723f2992157d", + "statement": "To develop a single package, navigate to its directory (e.g., cd libs/langchain_v1), run uv sync --all-groups to install dependencies, then use the same make targets (test, lint, format, type). Each package maintains its own Makefile with consistent targets.", + "evidence": [ + { + "resource": "repo://CLAUDE.md#L43-L63", + "version": "repo-lines-v1:sha256:18c23a068133429888d5554c52abc7fad7af4e7cfc99ede025695ab2769177cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBlOGYyN2U5YmQ4NzAwYmMxMzlhNWEwOGFjMWRlZTY1MTZjYTE4NDBhMjA1YTZmN2U3MzJhZmVhNzE1ZTk2MTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3YzA2YzU2MWQ1YzMyNzM0MzY4NTI2OGE0NmIzMzkxZjM5OThlZWU4OTBkMmJlNTFmMGQyMWNjNGE1M2FlNmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE0ZTBkNjliZDQ4MjFmNTY2NDVmMjAyZTMxMDA5OWI4NmM0ZTVlYjkyOWMzOTQzNDE1M2U1Yjc2Y2M5NjY5MzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ4YWEwOTVmYTY5YmU0NmRmODUyZGRhMjE2NDcxMzFmMmIwMmMyYjIyZTIzOWY4YzdmZGUwZjJkZWJmZmM3OTcifQ" + }, + { + "resource": "repo://libs/langchain_v1/Makefile#L1-L72", + "version": "repo-lines-v1:sha256:44c2dc216e86ae2b3042380be22516c1f63aa24c56c241fd72dbf5e46664ec91:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg1MzMwNWUzMmQ2N2ZhYzBiYzIzYjQyM2Y0NWY3OTg4MDIzZWE0ZGJkODI3NDc5OTliNWE0OTU0YjRkYTcyNjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxMDUwYTZhZTRkZWFkNTAzM2Y3ODQ5YzcwMjRmYjFjYzYxZDgwNDhiOTMwNTc0ODkyNGYxOGMwMmU0NjM5ZGUifQ" + } + ] + }, + { + "id": "claim_688e77b72f194e6d8f8521b36ec431ce", + "statement": "The langchain_v1 package runs integration tests using Docker services (PostgreSQL, Redis defined in tests/unit_tests/agents/compose-*.yml). The make test target automatically starts services before running tests and stops them afterward, managing exit codes properly.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/Makefile#L10-L44", + "version": "repo-lines-v1:sha256:c0433ebed594f01b0643c125280438a8183bd59aed0a94fdd5e84113fbccc255:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMyYTMxYjRjNTM4MzU0MjU1YWM5ZDllODJmMzgyYzg4YzAzYTQ1ZjZiNTg4MGJjNjQ3NTNlZTUyMmVmN2MxNDciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyZTNjZGE3ODE1MTM2MDE1MmI4YWM1ZWEwYTdkNmU1NWJkNzQxYjM1YWU1YWE3ZGVjMzU5M2ZmOTlmNzY4N2QiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc0ZWJhYzU1OWIxOGIxZWE4ZTkxZWUzMGM3YmE0ZWMxMGY4YTU5OGQ5NDNkYThmZDI1NDRkMzJlNGZlZWYwMDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVjYTkyZmFmYjU5MjhjZDgyOTUzZGU5OTNkMjBjNTIxZGMxYjdmNjhmN2E1ZTE2ZWRiOGFlZmFiZGJhMGYwMGIifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/integration-tests.json b/openwiki/.claims/integration-tests.json new file mode 100644 index 0000000000..24f2f9a695 --- /dev/null +++ b/openwiki/.claims/integration-tests.json @@ -0,0 +1,184 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:8bf3d528d2fe2628f24d10c54b6445ad11f8ec7abae99049815d037c83fdaf08", + "claims": [ + { + "id": "claim_2b394255185b4645a1dcf0e8c628a197", + "statement": "VCR (Video Cassette Recorder) is the HTTP interaction recording and playback mechanism used in LangChain integration tests. Tests record live API interactions once with VCR, which scrubs sensitive headers and secrets before saving the cassette; subsequent runs replay cassettes instead of making live calls, enabling fast deterministic testing without credentials in CI.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/Makefile#L40-L43", + "version": "repo-lines-v1:sha256:11e8869cd9ad3d186371833270e57f6b9b33e6bfb1fb7b21204584ccecc8bddf:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYWE5YjMxNjhmZmFkM2U1NDQwMzY3Y2Y1NTg2OTAyZTE1NGNlMWJmYWJmOGM5ODlhNjUzYWRiZGQ1ZDgxZTU3OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDhiZTQwMTJkYzZlODBmMGRhYTAwNWMwYWI5MDBkODY0M2UyMWE5NTVkNDZiYTlkZGJiZDk2ZDVmNDM5Zjg1ZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDgwZjAyNjdlOGNlNzQ5ZmY3YmVhMWQ0NGJmZTc4MmMzZWI1NjRlMDIxMDVlZGZkYjQ2MjNlMjU2ZjA5ZDQwYyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZmNlNGVlYjJlZGM5OWFiNzIxYTJmNTJhOGZhODc5YjM0ODY1MzUzNWViZTA4ZTUxNDdmZmE2MzkwM2IxZjRkNSJ9" + }, + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L1-L249", + "version": "repo-lines-v1:sha256:b85e00ecaa85790473365ec6ee983a68cac7244ca3be79b50cde32a75f250676:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQ5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjZjIzNDc5YTZiNDFhOGU5NGRkNTk5OWUzNjgzNDFhMDRiMTM1OTQ3OWZiMzM4MTA4MDk2MGI5YWVlMjJlMjkyIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1Yjg0MzIyZDBmMzg1NDQ0M2U0YzU1ZDNlMjkzNmNkM2QwZGY2OGJmMjlmZWVlYjE4OTVlNTkwNGQwM2I4YTg0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MCwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/conftest.py#L130-L143", + "version": "repo-lines-v1:sha256:0144da3602191e08b1c7c3a3d0ea02abb6d35b5ffbf3a7e28f89554b904f1087:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxNzhlNTQ1YjAyMTRlNDM2NDIxYTRiMzBiZDQ0NThjNDgxZGY5OTY5NWRkNjRmZjhkNmM1MDkxMTZjMmE5NTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQzZDE3MWRlNzU0MzFhMDIxYTBkOTRmYTc2ZmFiZjBkNzEzOWM4OThjMWEwYjQ0YjQzY2E2MDkwM2ZhMWI4NDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyMzc1ZjdjZDVkMWMzYWI1YzFlMjE1N2ViNjFhNjRhMDRlOTZiZDA1YTg1NThkYmY0MWRmZWUxMTE4YjcxMDcifQ" + } + ] + }, + { + "id": "claim_500db96bd1a849b0bf87fcfc658df506", + "statement": "Cassettes are stored in `tests/cassettes/` at the package root or in module-specific subdirectories like `tests/integration_tests/chat_models/cassettes/test_base/`. The `vcr_cassette_dir` fixture dynamically computes the correct cassette directory per test module.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/tests/integration_tests/conftest.py#L28-L33", + "version": "repo-lines-v1:sha256:c07f93990d02fa5f77fa71acb95580ada8744a98c94f91d72e1381f0077d3147:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWI1NmVlNDI4YWJmYjA5YjhmNTYxMmYwNmVjMTMwMDdlZWM4MWNhNTQyNWZmNmIyMGNhZWZmZjFlYjhjMDQ2MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYWJjZDJhYmU5MTg5M2Y3YmI0YTQzYjE2NDJkZjA3M2VkNzAyODdlOTNmNjE1MTY4YzE1OTNjNDM0ZDE5NzVjMiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDFjNGI2NjE0MWY3MWE4OWJmOGRhZjNlM2E1YTNjMTA1NTgxYWZlYTZhYmNmYTczYTlmNWVjYTE3YmExN2IxOCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" + }, + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L1-L10", + "version": "repo-lines-v1:sha256:178a83406b111cae8b9945220d636751deb8ed3bd88e5f84b74a590e38709789:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmMjM0NzlhNmI0MWE4ZTk0ZGQ1OTk5ZTM2ODM0MWEwNGIxMzU5NDc5ZmIzMzgxMDgwOTYwYjlhZWUyMmUyOTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVmZjI1MDY3NDRlNzQ5NDAzNzkyODEwNTU5ZTYwNTkzNGRhNzUzMmFiMTcxYmQxNTdkN2E0ODBjNTgzOWU4YWEifQ" + } + ] + }, + { + "id": "claim_54d8ddd8a7164b23ad0b43dcda38d1b6", + "statement": "LangChain scrubs sensitive data from cassettes before saving via multi-layer redaction: request/response headers (authorization, cookies, organization headers) are replaced with PLACEHOLDER; request/response bodies undergo recursive JSON parsing to redact OAuth secret fields (access_token, refresh_token, etc.) and JWT-shaped strings; binary payloads (PNG, JPEG, PDF) are skipped for performance since JWTs are ASCII.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L13-L163", + "version": "repo-lines-v1:sha256:0585222ed51eed033d0d51b9e73183d4c7848ad754faa70ae3fc38813a59a6ca:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjNWJjMjVmOTBlNjI4MWQyMWIyOWUyY2M5YmJhYzdiYzQxYTU4MzRiZjhhNTdkOTQzMDMyMzk2M2QxZTQ3YThmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJmM2NjMWQwNTZiYWIwYTc1Mzg1ZWY4ODg1MzRiYTdhNDFlYzY0NTAyODAxYmRlZmI0MjUwNjliYjJkYmNlMDBhIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlNWMxYzQ2MmUyYTM5ZDc0NzU5ZjVlMWIxMjZmNzY4M2EyYjczMDExOGUxOGY1MTIyYjA4Y2VkZWViNjUwZTIzIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJkYTc3MDc4NjBiMzhmZTcwNmZmNTJkZTNkNTUzZGFhN2QwZjI5ZDI1NWI0M2ZjMTE4OGMyMjQ1OGIyNmRjOWViIn0" + }, + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L166-L187", + "version": "repo-lines-v1:sha256:a1feed4edb565ee0f5aa60ad840e641a44ad15edc95a48b2bf44baab447eb762:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI5MDlmMTA0ODRlZjBmNzVkZjNkNThmYzMxMThlNGU1MmQ0NjRiNmNmZWY0MDA2NzRhNzNhMGYzOWE4NTU2YzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcyMDg1MzI5MTBjZmNlZmU1ODcwM2Q0MzQ0NWE5YzIwMmIxMjQ4NzcyZjE2ZWY1YTE2MzBlNjJmMjVkZjAyNzQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVjMTFhYmZlNWIyZTY0MGY5ZWM1MWQ5MWI2NGZmMTgyOTExOWMzMjg3ZDg1NThiZGE4ZjQzMmFjNzdmNTVjYzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyMzc1ZjdjZDVkMWMzYWI1YzFlMjE1N2ViNjFhNjRhMDRlOTZiZDA1YTg1NThkYmY0MWRmZWUxMTE4YjcxMDcifQ" + } + ] + }, + { + "id": "claim_1cc580eab7d64d7f819732d066840145", + "statement": "Integration tests load credentials from `.env` files (located at `tests/.env` or `tests/integration_tests/.env`) via `dotenv.load_dotenv()` in conftest on import. Tests access credentials via `os.environ[\"API_KEY\"]`. Tests without an API key in `.env` fail at runtime if they attempt to make live API calls, but cassette-backed tests run without credentials.", + "evidence": [ + { + "resource": "repo://libs/langchain/tests/integration_tests/.env.example#L1-L10", + "version": "repo-lines-v1:sha256:7d2d09a71f21529ce791fb25eefc3b76ba0eb543269183caa8f2a08b5aefdb31:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdhODA0YWQ1NmQ4YjU0OGYxZGVkNjUyMDJiOGVhMmI2NzVmMjAyODI0ZjAzMGYwNWVhODRiNmMyMDdkYjQ2ZDQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ1MTEzMGVjMzI4M2I5NDYxMjMzMzM1ODAxMzNjYWIzMjE2MjgzZTQyNTVlYjQyNzJhMWQ1Njc3ZjYxOTE0NDMifQ" + }, + { + "resource": "repo://libs/langchain_v1/tests/integration_tests/conftest.py#L1-L26", + "version": "repo-lines-v1:sha256:061351d62d2c74fcabfd6e23faf1bac12b01a02fb00b7e9dd90279fec4dfcc06:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNjYjlmZmNjYmE1MTQ2YjJkZDE3N2EzMGMzMDU3YTQzOTVlYmQ1Yzc0NWQwNzg5ZDJlZDk1ZDBjZjRhZmUyNzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYzZDU3M2EwYjY2M2JlYzc5ZmYwMGVhNTFiOTEyNmYyYWY2NjBhOTg2NzUyMWEzZjA5YjJlZjdlNTZhZWEzMzIifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/test_base.py#L47-L56", + "version": "repo-lines-v1:sha256:da4d3e9908cb823935c901a1344645b98de1cfcb0f5c7a2ec32c29c6f594ce51:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxZDUyNjM1ODI4ZDQ0NzEyOTk3NjkyOGY4YWFkMmJkNmVhNDI1ZGZhYzdhM2I1YzYzYTBmMWQzMTIzZGU0MTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5OGIwNjNkOWI0N2U2NTM3OTYyNTU3MmNjZTE5NzY2OWQ1NzY3ZTEyYzE4YmUwNzU2NTJiOGNkOTgxY2U4ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQwYzUxNmMxNDA5MDg3Y2EwODFkOTg4M2E5YzdhMWEyZjM1ODIwYjQwNjUxNmE1MmIyYThlZDc5NGMzNDE0ZDYifQ" + } + ] + }, + { + "id": "claim_91e6083fe38f4c06b675d1010d7577c7", + "statement": "Integration tests use pytest markers to classify test types: @pytest.mark.vcr marks cassette-backed tests that replay recorded interactions (run in CI with --record-mode=none); @pytest.mark.scheduled marks live-API-only tests that are skipped in CI unless running in a scheduled workflow; unmarked tests are offline and don't require credentials.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/pyproject.toml#L141-L147", + "version": "repo-lines-v1:sha256:f4860849b111f1c575475888e3d06804a8c2627ad1672362e56cb1f155ee634c:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODY3NmU2NDE0NjVhNjU1MWU5ZTljZmZkN2ZhYTFlNmE0MmY5YjcyNDgwOTgxM2M5MDNkN2VkY2I1MmYzMDA2ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjk0MTZlMDlmNzhiYTMxNmMwMzI1NjgyMTNiODU1ZWU3NTZlNDE1NjVkOTE4ZjA5NTE5YzgxZmQ2MjBmNDA2MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYThlY2FhYTA1ZTE0YWZkNGJlNTJkOWEyNDhhYTc4NzEwNjUyY2YxZjMxZTZiNmE3ODZmMDVmNjliZmFhMDlhYiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMzk3NGYzMmE2OTI3ZWNkN2U0MGI2ZTk3ZDQyNDhhNjczNTM5OGVlYTY4MWE2M2FkZTMxMDUxMGFlYTY0ZjJmZiJ9" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/test_base.py#L58-L78", + "version": "repo-lines-v1:sha256:26638124d864750acfe43d1ed037e3ab197666170c26078a25a9055fce2c9342:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhYTVmMDdkNWFiZjU5NDNiM2ZiMzA1ZjI1MjAwY2IyNTBjOTQ2ZTUzYmNlODczOTAwNzFiMWU1MDllYWQxNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE1N2E4OGI0ODlkNjgxMjhkM2IyNTY0OTNkMTJhN2E1ODc5NTczNmQ1ODI3ZGJmZjA3ZWZiMmFiZmE1MWVhNTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjczZDE1OGFhYWM0ZTAzNDRjOThiOGRkZDIwNzZkMjI4NTQ5OTE4YzBjYTJlODUwZDM1NGY4OTgyYTk4NzM1NTEifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/embeddings/test_base.py#L38-L51", + "version": "repo-lines-v1:sha256:e75969cbc6a78340e6243cd951ea433be14001e37af5f9fb9505071896fea8c1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI0NTMyNDc0MGJmZmMzYjk1Zjk0ZGIyZDdiM2Q1MGU1ZDkxMDEyNmUzNTllOTYzOGU3Y2I1NTIzZjFjMjQwM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmY2Y2ZDYxNzNhNTA3NTQwMjNjYjFjOTVjZDBhYTU0YjQxNGYwY2M0ZGY1NGExNDQ4MGI3NjI3MWZiNzE5ODEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI1MDVkOGM1NGY2NWZhNWNlMTc1NjA2YWIzNDNhNWU0NjVlYjE5ZDY2MWJjYTkzMDE1NDlmODlmZTYyMzBjNDgifQ" + } + ] + }, + { + "id": "claim_3f784b2f297348b2ae4b083cd407a10e", + "statement": "Cassettes are recorded once via `pytest --record-mode=new_episodes` with a valid API key in `.env`. VCR intercepts HTTP calls, scrubs sensitive data via before_record hooks, serializes to compressed YAML (.yaml.gz), and saves relative to the test module. Cassettes are committed to the repository and replayed in CI with --record-mode=none.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/Makefile#L35-L43", + "version": "repo-lines-v1:sha256:fecf43d133a0b72243df932394549e0a5902eb879f7164c7eed7d6d2edadec63:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTk0ZWRmM2ZjZTEyMmUxZDcwZGRhZTQ0ZDU1ZTNkNGE5MTVkNzc5Yjk1YWJmYzkzMDIyMjQxNzQ0YTA2NTljYyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDhiZTQwMTJkYzZlODBmMGRhYTAwNWMwYWI5MDBkODY0M2UyMWE5NTVkNDZiYTlkZGJiZDk2ZDVmNDM5Zjg1ZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTFhMjk4MDZjNTZhZmI4YTVhY2IyN2ExZTFjNzJjZDAzOWZmYWNlNTlmNzgzODE1YjhlZTZmYWUwNGUyN2M1NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZmNlNGVlYjJlZGM5OWFiNzIxYTJmNTJhOGZhODc5YjM0ODY1MzUzNWViZTA4ZTUxNDdmZmE2MzkwM2IxZjRkNSJ9" + }, + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L190-L204", + "version": "repo-lines-v1:sha256:e1eddc5a41b085268d3db96f1152e60ae2dd6b69320f898d3da34980bdfbbe5d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzMDM2NWQxNGM3ZGFiM2Q2NWIyMGVlYjNhZmU3ZTVkNGUzMGU4Zjc5OGU3OWFkMDZlNTk0MjI4Nzc0YTc0ODYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk5MWU3YzkwZDY2ZTYzNjZmODcxM2EyZjkzMjE1OWY5ZjAxYTk0MzU2ZjIzNGNmODk4YTdjNDliYzdkOTlkMjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjczMDMyNWY5ODBiNGFiZTBhNWJmZGViMmQzZWI5MzQ4NzZkM2EwNDMzMjU2NDQ0NzJjMTM2Y2NhMDJiOWE4MGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRjOGExOTM1MTY1NGUwMmMzY2E1NTBmOTFhYjRmZTkxMGNlN2Y2M2E2Y2YzYmY2MWJiYzY1MWIxZjI1ZjllOTUifQ" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/conftest.py#L47-L82", + "version": "repo-lines-v1:sha256:df5c8cedc06db79e7f2b48dae77d72b15c3e1b4dc9663a7c5e076f731c40bfc2:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMyOTQyMzQ1NjE1YzAwYjE1MzgxZTYyODg2YmRhM2ZmZmY4YTU1ZjljZTVmYTAzNjBjMmI1OGI1OTg4MWU5ZjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiYzI5MmY2NTFiMGZmNzY3M2U1YTY5NWMyZjg4NGQ2MzJkMWIyMjY1YjQ5ZGQ1NmE1YzE0YmQxM2E5OGY2ZTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI5ZWUxOTMyODRlODJmYjc2YWY0ZjRmNTgzZTQzZjA1MDdiNWYxMzJiMTY0ZTZkMzUyM2JhZmI5NGMwYzdiMWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIxN2U3ODJhY2U3YzhmNTk2YjRlZmZiMTI5ZmQ0ZDljNjFlOTk4ZmRkMGZhNDdlN2EwNjY2ZjIyM2MzNTdlZDUifQ" + } + ] + }, + { + "id": "claim_30e157b2c11a4c03b076a66f28517ea7", + "statement": "Integration tests support parameterized testing of multiple models/configurations via @pytest.mark.parametrize. Each parameter combination generates a separate cassette file (e.g., test_invoke[model0].yaml.gz, test_invoke[model1].yaml.gz), allowing independent recorded interactions for different model variants.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/conftest.py#L1-L50", + "version": "repo-lines-v1:sha256:eddffa985f00658447b0e5984bb7a938e635dde6458d1bacce1465edb7ee3053:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4MjU5MGRjNzZjZWI0YTMzYTJhMzhkOGFkYzgyNTFiOGM3MmU1MDlhNjZlYzg5NGQwOGE2ZDdkYzk3NGQxOGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU4MThkNzI2MGJmZTcwMjc3MzIwMzVjNzE3NWNlNzZmODA3M2Q5NGNlZmZmZTlkYThkNWNiMjRhZGUzYzg5NzEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxYWNjZjkwNGZhNmM3MGY4ZmZiYjQ3NTVhMzc1MGE0MTFiNzZjZTJiOTE4M2M4MmQ4NjQxZDU1MjA2NzYxZmYifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py#L16-L60", + "version": "repo-lines-v1:sha256:4abaae78a2c7e532f9fd3b3e1a05a877804ba3311b29f8e072acd915c3be821f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJjNWExYzAzOWM1OTM5MGMzOTY0MDlmNTU3OGUzNDU4ZGUzOWI5ZmZjYzY0OTNkZjA4MWM0ODY2YWJlYjI3NmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxMDQyNGJkZTNhZDkxZmY4ZGFlYTgxYTRhYTUwNGIyZmU5ZDM3OTIzZjU5Mzc5MTUyZDU0M2Q3YjRkZWFmNzEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjYwMjM0YTQwZWNmNjU1OTUyOGMzODQ4ZDFkZjdkYWYyMzE5ZDFhNGNjOTU5ZWUxNWQ0MzllMjJjNmJmY2U0OWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkyMTU5ZTRjNTBlMWNmMTlmM2UzZDM4YWFmMTMwYTI3ZWRmNTg3MDVhZTVjZTFhMWYyMjBiNTAwOGIzMzczYWIifQ" + } + ] + }, + { + "id": "claim_f2f942acdaf344f8a7dd5232ded2f2d9", + "statement": "VCR integration tests support both sync and async code; async tests use @pytest.mark.vcr and work identically to sync tests because VCR intercepts the underlying httpx/requests library. Streaming tests (via stream() or stream_events()) are fully supported; VCR records the stream sequence and replays it identically.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/test_base.py#L182-L200", + "version": "repo-lines-v1:sha256:fafef0a13e0016604f3c165e8084f8715614912c9c36a21559ecaf8514dc10a7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhYTVmMDdkNWFiZjU5NDNiM2ZiMzA1ZjI1MjAwY2IyNTBjOTQ2ZTUzYmNlODczOTAwNzFiMWU1MDllYWQxNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM0ZjhmMTJjZjhkODE1MzI2ZjhmMjc5MTc4ZjViYWVhNmU0YmFjNmY5OTk0MmEzNmMxNmVmY2NiNjA5Y2E1ZjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzMzkxMDBjZmYyYzAxMDEwY2I1YTI1ZjI1ZThiNzVmNTAxNzc2Nzg4MWUyMGIwMzYwMDE1ZTVhYjQ2MjU5OWQifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/embeddings/test_base.py#L53-L65", + "version": "repo-lines-v1:sha256:12ea5a2af5c6c9d1bc6c5d3b119a4244a7aa427067d71dbf77c3936af2c2c30d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI0NTMyNDc0MGJmZmMzYjk1Zjk0ZGIyZDdiM2Q1MGU1ZDkxMDEyNmUzNTllOTYzOGU3Y2I1NTIzZjFjMjQwM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI4ODFjYjgwMGJhOTI5YzcwYmY5OWY5YjRiZTYyMmZjYThhNDY1NTkyNTkwMzVmNDJkMGMyZjA4ZjBhY2RhOTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjViNjFlNzliYjM1ZjlkZTFiZWJiZmZhNTIwYzgzYzEwZGM4YjgzZmJlZWZjY2E1YjA5YjNkN2U5NWE5OGU5NjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQxOTNlNmJiZjNkNWE0ZTY5MTM4NzBhMGUzNjUyNjAxYzkxMDAzOWVjYjZkYjI4MWM0NDNmZjA5YTE4N2M3NmEifQ" + } + ] + }, + { + "id": "claim_2b49d8e4a26b46658f273e50b4c1c2ed", + "statement": "Each package's conftest.py extends base VCR configuration (from langchain_tests.conftest.base_vcr_config) via a vcr_config() fixture. Configuration includes record_mode (once/new_episodes/none), match_on strategy (method/uri/body or json_body for JSON-ignoring-order matching), filter_headers, before_record hooks, serializer (yaml.gz), and custom matchers registered in pytest_recording_configure().", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/conftest.py#L190-L249", + "version": "repo-lines-v1:sha256:cd6487781a7c4c4b6ded25db13dc8947476013ae99271f7d20a8d84bdb5faa01:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzMDM2NWQxNGM3ZGFiM2Q2NWIyMGVlYjNhZmU3ZTVkNGUzMGU4Zjc5OGU3OWFkMDZlNTk0MjI4Nzc0YTc0ODYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjViODQzMjJkMGYzODU0NDQzZTRjNTVkM2UyOTM2Y2QzZDBkZjY4YmYyOWZlZWViMTg5NWU1OTA0ZDAzYjhhODQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjczMDMyNWY5ODBiNGFiZTBhNWJmZGViMmQzZWI5MzQ4NzZkM2EwNDMzMjU2NDQ0NzJjMTM2Y2NhMDJiOWE4MGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/conftest.py#L130-L155", + "version": "repo-lines-v1:sha256:e3d0704dff8fca8d0a61d64b2b6b1bd421654204ab5faef64b64dd5b52df2ddf:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxNzhlNTQ1YjAyMTRlNDM2NDIxYTRiMzBiZDQ0NThjNDgxZGY5OTY5NWRkNjRmZjhkNmM1MDkxMTZjMmE5NTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxY2I2ZmI5YmVjNzJmZjA3ZTY4MDU0N2M3NTAyMzZkYWZmNDMzOWVhZTY5ODBkM2IwOTQwMmRkYmE4YzcwOTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQzZDE3MWRlNzU0MzFhMDIxYTBkOTRmYTc2ZmFiZjBkNzEzOWM4OThjMWEwYjQ0YjQzY2E2MDkwM2ZhMWI4NDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_150d3ea6225d45dd9187d0a97ab6047b", + "statement": "OpenAI Codex tests require special VCR handling in CI: conftest.py skips Codex tests at collection time unless running in --record-mode=none (cassette playback), and uses monkeypatch to stub _FileChatGPTOAuthTokenProvider token methods with fake in-memory tokens for VCR playback without requiring on-disk OAuth files.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/conftest.py#L1-L14", + "version": "repo-lines-v1:sha256:e1ec6b6d802926f2b1cac2647b9c3accc444f2b800170c6878377c812edd08df:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ4MjU5MGRjNzZjZWI0YTMzYTJhMzhkOGFkYzgyNTFiOGM3MmU1MDlhNjZlYzg5NGQwOGE2ZDdkYzk3NGQxOGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1YjZlODcwNmI0MzA0N2YyYTZkYjY3ZjEzMjYzODU5NTYwNGU2N2YzNjk2Njg3NmNlMDE1OGYwMDUzZTZiZDEifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/conftest.py#L66-L92", + "version": "repo-lines-v1:sha256:57f36b10f5a271c6395a3103efe0146cbc3df80328363db90b11be8cf4cd1625:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmYzYwZGExMTM1NTc1OTRhYTlhZmUyNjJmYjUxZjNhM2RlMWNhYTdjNDdhNTg4NTExYjdlYjA0MjY3OTY0ZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiNDZkM2U4YWZjMzk3MWNjNzIyODc1N2Q5ZGI2Njk4ZmNmMGU0MzUyODAxNDc2MDcyMDBkZDhmMjBhODk3OTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MDE0OWUwNjlhZDY3ZWNjZjQ3MzY0NmZmMWM3NTAwNTBkMzI5ZDExMDAxOGRjNTc2Njg3OGVhZmExZDYwN2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFlZjExYzliNzE2N2MwYWRjNDJmZWE4NTRhYjYzNDk5NzcwODE0YjBlNzE4MTAzYTllYzBmOWJlN2FmNjk5NzkifQ" + }, + { + "resource": "repo://libs/partners/openai/tests/integration_tests/chat_models/conftest.py#L95-L147", + "version": "repo-lines-v1:sha256:290ba4ef134137e43ce35cf85c1bbed29bf210fcd78fd848944179e080537c58:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmMDg2NmY0MDExZTU4NWZmYzA5MDkyZWRmMDgzMmUxN2I1MmY0YzkwYzRjYTBiNDJkY2ZhMDUwZmEzNzViNWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE1NDhjOWVjNjdmMzU3OWFhMTYyMWFhOTcxOGFmNjVjNGJkMjY2ZmRlMzQwZDA5ZWZlMTYyZTk0M2I5Y2IxN2YiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_7d605667c6f04ced95e6049010ae706f", + "statement": "LangChain integration tests can be run in multiple modes via pytest record-mode flags: --record-mode=once (default, records new cassettes, replays existing); --record-mode=new_episodes (always records, overwrites existing cassettes); --record-mode=none (playback-only, fails if cassette missing, used in CI).", + "evidence": [ + { + "resource": "repo://libs/partners/openai/Makefile#L35-L43", + "version": "repo-lines-v1:sha256:fecf43d133a0b72243df932394549e0a5902eb879f7164c7eed7d6d2edadec63:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTk0ZWRmM2ZjZTEyMmUxZDcwZGRhZTQ0ZDU1ZTNkNGE5MTVkNzc5Yjk1YWJmYzkzMDIyMjQxNzQ0YTA2NTljYyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDhiZTQwMTJkYzZlODBmMGRhYTAwNWMwYWI5MDBkODY0M2UyMWE5NTVkNDZiYTlkZGJiZDk2ZDVmNDM5Zjg1ZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTFhMjk4MDZjNTZhZmI4YTVhY2IyN2ExZTFjNzJjZDAzOWZmYWNlNTlmNzgzODE1YjhlZTZmYWUwNGUyN2M1NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZmNlNGVlYjJlZGM5OWFiNzIxYTJmNTJhOGZhODc5YjM0ODY1MzUzNWViZTA4ZTUxNDdmZmE2MzkwM2IxZjRkNSJ9" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/conftest.py#L130-L143", + "version": "repo-lines-v1:sha256:0144da3602191e08b1c7c3a3d0ea02abb6d35b5ffbf3a7e28f89554b904f1087:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxNzhlNTQ1YjAyMTRlNDM2NDIxYTRiMzBiZDQ0NThjNDgxZGY5OTY5NWRkNjRmZjhkNmM1MDkxMTZjMmE5NTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQzZDE3MWRlNzU0MzFhMDIxYTBkOTRmYTc2ZmFiZjBkNzEzOWM4OThjMWEwYjQ0YjQzY2E2MDkwM2ZhMWI4NDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyMzc1ZjdjZDVkMWMzYWI1YzFlMjE1N2ViNjFhNjRhMDRlOTZiZDA1YTg1NThkYmY0MWRmZWUxMTE4YjcxMDcifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/mcp-integration.json b/openwiki/.claims/mcp-integration.json new file mode 100644 index 0000000000..ef563972da --- /dev/null +++ b/openwiki/.claims/mcp-integration.json @@ -0,0 +1,256 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:39684cc4e8be6978f803b85b99406b6a877eaf9e8c8b964279a413f854340751", + "claims": [ + { + "id": "claim_a20f2f32ba21436d869cf463fc4e93f6", + "statement": "MCPAdapter wraps FastMCP clients and discovers tools via fastmcp.Client.list_tools(), converting each tool to a LangChain StructuredTool with async coroutine and content block conversion.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L125-L172", + "version": "repo-lines-v1:sha256:522fc669e156571f02b6a3683cd982f55d4ad22f3468f7b48e991a148877320e:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlMzNjYzBkZDhkMDU2NjRjYTQ5NDc4YWU3OTNjZjdmYTE0NThlZmE5YjM4ZTA1ZGRiNDk5YWIyOWIzZTFmM2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFiOGYwNThmNzdlYjg1NDA5MDYxZDg2YjIzMDg5MzQ2MzBhZTExNzlkMDkwNDdlMTRkMzRkOGNmYTU4ZjU3MWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEzNjY0MDU1M2U5ZGEyZDllMTYxY2RmYWVhYzMwYjM4NTY1MmQ4ZjE3MjE4OTEwOTllOTRiYzM5Y2JiNmJhNGMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L221-L242", + "version": "repo-lines-v1:sha256:8c8f286728fc9aa7955ce4e7a05920a6a04a6800acbb6d7e328ba2f703cef134:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkM2M5ZGRjYWQ1MjNiMDZjOWQwY2RhYjRhYzUxODA3MWYzNjMzMzViNDk0NzFmNmNmNWQ2YTE2ZjNhNzE0ZmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkwMWMwYThiZDk4NzRhNDBkMzFkYmYxMDdiMGEzNTQ5MTQ5ODIzNTJkMDY5ZWVmNDcyNDNiZDExZWY3ZjYzNjIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM3Mjk5ZGUzODI4MDM3NjcxYWE1YjJlZjhiYjAxNWY3YmM4MWQ0MTU1NWE4ZjNmNDY0ZGI0M2YxZThlNGM0NDYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhjYTZiZmFkYzc2OWJmYmM0ZDI2NmExMTI2MmIxMzhkMzdmZDEzZDg2MDk1MDE3YThmNjdmYzE2MDNlZWJlYjMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L212-L282", + "version": "repo-lines-v1:sha256:00a655fa2836605e00b588e1d4ac8d16290e6182e8caa5fa76c26a2a925ffeac:eyJzZWxlY3RlZExpbmVDb3VudCI6NzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc5YmFiYmE2Yzc5NzMwZWZlNTY1ZTM0OTFjMGNkOWMzNWM0NGQ1YzJjMTg1MjEyYmVkMmYwMTdiMDg1NWNiNzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNiYmRmMTVlYzYwZTVlOTkzZTBiODA0NDBlOGY0ZmUzYTE5YTAxYmUxYjk5ZmI5YTQwMjVhMGE2YTMwMzkxYTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImExYTVkMGU3ZWNjMzc0YTIxNmM5ZTAzNzZhZGZjNGE1YWUxOWQzZTRhYTMwNTBmNDA4NDkzNjRiNDJkN2Y1OWEifQ" + } + ] + }, + { + "id": "claim_2122d7d33c7b4b4b98dadac5feae562f", + "statement": "Tool targets may be a URL string (http/https only), Path (stdio subprocess), FastMCP instance (in-process), pre-built Client or ClientGroup, MCPConfig dict (multi-server), or ClientTransport. String validation rejects non-URL strings to prevent silent local file execution.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L136-L144", + "version": "repo-lines-v1:sha256:1e21bdfda03641ce502e1e7145ffbd0705f03082662c4aabb5399693e2e9221b:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTMwN2VjMjUyYjY3MTlmNDFmN2UwYTJhMzMwODNlZDY3OTM4MTllMjVkZWEzMTVlNDk0NmVlODMyNWFlMjExYSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZGI1MzU0ZTI5MDI0YjgwMDBhOWE0YWE5NTM4MTBjOTAyMjYwZjY0ODZiZjQ1ZDIwYmVlMjM1MmYxYmYzZThhNSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmU1Y2I2MTU4ZDEwZmYzZGFlOWM1YzFhM2MwZTM5YWFjMmIzZGVmOGIxYmQ2NjQzMmU3ZjZkYTBiZDM0NjA3MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDQ1YjA2ZjYwMTU3ZmM0ODcwYTc0M2I2ODIzMWQzMDdiNmI2NGYzYjg5NzQxOTI5MmRiZTcxZDMyNDI5MDE5MyJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L47-L69", + "version": "repo-lines-v1:sha256:dde556a376a7608e297f970254a8876d7af4b10ad211532d2279d534bb945f29:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNlYThlZjZjNjIxMzlhY2Q3MWU2MmVjYWQ4ZGNjNjRhZWY3ODY3ZWM1ZTdmNDdhYjUzMWQ2ZGJkM2Y5MWRkN2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFmZmEzYWFjNjkyMGE4MGMzZDYxZmQwOWJkNTUzZGQzYzY1NWFlN2QyNzg5MzFhNjc4YWM1ZjZlZjhjY2ZiZTciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImEyNGYwMjJkNDQ4NmZkMDExNzM1Nzc4MmFhZjNmZjMyNzFiMGQxMDBlMTQ3YzJkYTA0YWI5ZmQ3MDNiMTg0OTQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L84-L123", + "version": "repo-lines-v1:sha256:4701b5871affb98b6977479b963cf8cfd5db05a9b3a4b2735dccb4364d41eca8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI0ZmUzYTFlZDIxNDU3ZTEwMzRkZjU3MmEwMzY0NzFlOGIyYmM3YTMzODRmNjljN2I1M2VmYmQ3NmNkMjAxYWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY3MWExMDFmNGMwNzI3NmE1MzU2OThlMjJiNTI4YTIwMGMxMDhlOTlmYjBlYjI0MTVkOTEwNjk3YmViMTk5ZjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI0MWY2OWZkZDhhOTU0ZTY3NTg4ZWZhNjY5ZTA5OTNlM2FhZDY2OGRjMjlmNmUyODI3MzJmYWNjYjYxNzA1YjMifQ" + } + ] + }, + { + "id": "claim_42dd7f62274f4fe3969bda5bf386a704", + "statement": "MCPAdapter arms each client with _arm_for_interrupts() to advertise elicitation capability, setting a sentinel callback. Pre-built clients with existing handlers are cloned first to avoid mutation.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L146-L157", + "version": "repo-lines-v1:sha256:bca7c5f85b5e94706ec8f14e04c9c256a05a4d72abca5297a24176f6b5f276b6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIxYmY2YzRjNGM4N2E0NDdjMzQ5ZGQ0ZDNjOGNmYTc5YTk5MDAzMTUxYmFjYmFkZTM0ODg0ZjU2OWZiOWU4YmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQzY2IxZTE4ZGFlNGZlZjQyYzliNzY1MDFiYjk1NTRjMTMxNWVlYTY4N2NmODk1MTA0N2M3MGQwYTA3NjE0ZDYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY3Y2RjYTc0MDRkNDQ2NTA0NTA0MmFmYTExNTRlNmYyNzBiNjUwYzAwMThlOGFiNjBhYzg5NDRhNGQxYzBjMzkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ0Y2UzMDQwNWI0ODk1N2YwOTQ1YWQyZjAwYzQwY2M1YzIxMzUxNTQ3NWEzOTY2NDhhMmM4NDZjYmY4MzlhYWQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L174-L200", + "version": "repo-lines-v1:sha256:a7c3a06c163bcf4e20d6e44446d36e8bb14135f5a0009971c6bd2341897814c6:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkN2QzYjgwZTRmNWE5ZTEwYmE3MTJmZGQ3MDQ2MzAyMzUzZDlmZGIwNWVhMDMxM2MyY2M3NjdiMjhjMTMyZTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2MmMwNTM4ZmVhMDkzMDE5ZmMwMDRjYjdiNjkxYzY0MTE1ZWYxZDYxZWUwM2M4NDZmMmMzODRiZmQ0MjA0NTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI3MmE4NTA2MzFiMjI5MGVlMmYzN2YwZGEyZDM0YjAyNjEwNWE0NGYzMDU4NmFkMGU1MTc4NWMyZjg0NTVmMGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM0Y2Q2MGM5NDcyYmRmNmJlZTNkYjk1MjBjMTRjNTZlZTEwZmU3YTRkMDA5YTMwNjZhODMzZjI1MmE5ZGQ2OGMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L246-L249", + "version": "repo-lines-v1:sha256:93c9ae62d315e7a19a2d582d5db33dace8b3d7424f4cfaf3916c39aee0437022:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZTk4Yjk1YmJhMDFkYmEwZjlkZTI4ZDRkNjZlM2MyZDMyNzliMWI2MGRjYjJmMGI0NWNiZmRjYjI1MjIzOGY5NiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNWRhMTRkNTA1ZjlmNDlkMWI3NDBkM2JjODYwZmVkM2M5NGU3MTQ3ZGRiNjMyOTk0YjhkZDcyZGEzMWY3ODk4MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMGZmMGQ3OGJlNGJjMTllY2Y4Nzk5NzcxYjFmZTZkOGIxNTEzMzg2NTZlZmZkYzMzYmNkZDNmNzU5MGVjMTU0MCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTM1NDcyM2FiMGJjMzBlMjQ5ZjQwZWY0YjdkZmNkZjI2NmVlMGVmZjI4N2NlN2E4YTdiZjQyMDk4MjczMDA0NCJ9" + } + ] + }, + { + "id": "claim_6c95307643e24437a209dfac13ad809a", + "statement": "as_langchain_tool() converts MCP tool schema to StructuredTool with args_schema, calls _call_tool_with_interrupts() if client is interrupt-armed, otherwise plain call_tool(), and converts result content blocks and handles errors with _handle_mcp_tool_error.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L104-L117", + "version": "repo-lines-v1:sha256:ebd4cd817c5920d623327b25456b1d194e5982d18b3fce449afaefc0b41bae70:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU0NTdmNTg5MzQ4NmEwNDQ1ODczOWU2MjhjNjk3NzAyODFhODJiNDA1YWU2ZjA5NjNmNGU3ZWExY2RhYWMwODUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgxMGFkODQ2ZTg0ZTI3ZmRjM2UwZTI0ZWUzMjQyOGNhYTVhYjNmNmI4MWQyN2RkZDNlNjkyNzg1NzQ4MWUyZjAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk2N2NjNTIxYWM0Y2EzMjExNDBmODliNjlhNTM4NjcwYzE2Y2FiYTQ5MjA2YzFkOGU2NTgwOGYxNjNkZDc5NjIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNiY2E1N2M2MTQ4MGQ3NzVhMDNhODE4MmJkMTE1OGYwOWRhOWIzYjE4M2Y4NGMyOWYxOTRlNjBjMzIxNmVkOTQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L212-L282", + "version": "repo-lines-v1:sha256:00a655fa2836605e00b588e1d4ac8d16290e6182e8caa5fa76c26a2a925ffeac:eyJzZWxlY3RlZExpbmVDb3VudCI6NzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc5YmFiYmE2Yzc5NzMwZWZlNTY1ZTM0OTFjMGNkOWMzNWM0NGQ1YzJjMTg1MjEyYmVkMmYwMTdiMDg1NWNiNzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNiYmRmMTVlYzYwZTVlOTkzZTBiODA0NDBlOGY0ZmUzYTE5YTAxYmUxYjk5ZmI5YTQwMjVhMGE2YTMwMzkxYTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImExYTVkMGU3ZWNjMzc0YTIxNmM5ZTAzNzZhZGZjNGE1YWUxOWQzZTRhYTMwNTBmNDA4NDkzNjRiNDJkN2Y1OWEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L259-L272", + "version": "repo-lines-v1:sha256:6573322ab6d14f91e4fd0986c45e0a37c1d687b3909ac46e82d21dd9bbb2d793:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdhYWFiNGRiNjY2MWUwZDNkOWJiYzgwMzVlZTVjNTU2NmQzZWZkYjBiYTM5N2VlM2MyYzkyMTYxYjkzZWJhMzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU5Y2QzNWQ4ZTgyNzQ1MmJmZWRlMzAzZjI0MzgzNGM5MmY5NmZkMWFkMTBmOTE3NzZjNzJhNDEyNDYxZDg5YTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE1NjFkNGU0NzQwNWIxNzI4MGE1MGEyNjhkYWY5MmJmMzQ3NDZiY2Q3OTQzMjI4MTFhMGU1OGIxMGYzNGJkYWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE2ODBhZjU5MDM2NWU1N2MzMjkwZDEzOGZhZmJiNGViNzk4NzcyODdhMjNmMjRlYTg1MDcxMDJlNGM4YzI1YTcifQ" + } + ] + }, + { + "id": "claim_d87907e8a9cf49ada4e4ee1991af708c", + "statement": "_call_tool_with_interrupts() issues call_tool with allow_input_required=True, loops while result is InputRequiredResult, filters requests to elicitations only, raises interrupt() with request payload, resumes on answer with input_responses, and repeats until terminal result.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L312-L379", + "version": "repo-lines-v1:sha256:05e3918e07f837ff89e0b8a1a2358fa3d060f8df483e23ddd3a37ca755128042:eyJzZWxlY3RlZExpbmVDb3VudCI6NjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkxNWNjYTU0NjEwNWFhZTY4ZjM0M2VjNGUyYzY3YWFmM2JlOGRiNzZlOGM2MGY2OWUwZDNmNTAyNzNkNDNkNmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhMTFjMDU2N2QwYTVmOWU5YjEzNWY2NWNkMjUzNzJkZmQ2Mjg3NzVmN2MxZWQyNTNmOGJhZDQyZGQyZDY2NjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcxNjU0ODgzNGMxYmMzODk2Y2JmNzQxNWQwZDJiMWViNWYyMTM5MzI1NGMxZmQ3Yzk1MTYxYzczOTVmY2M2MmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNiMjM2N2JkMGRmMGE2MmRmYzNkMTIxOTkxYjI5OGZlNGU5YzNkMzI2NzUyZTY5ODU1OTQzYjZjZjRiMjQ4M2QifQ" + } + ] + }, + { + "id": "claim_b32dacc492be40eb96c25993f2757c8d", + "statement": "_drives_interrupts() returns true only when client has _ARMED_MARKER and server protocol version is at least LATEST_MODERN_VERSION (2026-07-28), ensuring interrupt loop only runs on modern servers.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L252-L263", + "version": "repo-lines-v1:sha256:5321dcb55536811f4f0975b59e026d1cf83f3d2a5adf5acdecd6d9aa415b889e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA0ZTNkNTM5ZTdjNGQ4ZWJjYzgxMzNkOGFlN2I1NjIwOWE1OWZlZWI0YTFlMTQyYTc5NGI0MTliYjY2Njk3NTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJiZDQ4OWRkYjU0NGYyMTAwZGVhOTU2ZDQxMGFkNGQ5NTU3Njc0ZDg5MmE0YTczNDNhOWUxZDdiZDk5NTQ4MTQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5NGQ0MWRlMzhjNTRjZjFmOTYzMWI1MGZjY2M1MTQyNTU0MjY1YTUyNmI0MjEwNTZjZDM1MDY4MGRjOWI4Y2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA2YzRhN2E1NmJkZTE2ZmQyNWU4NWI0NTRmNzY2Y2IwODAwYWQ0MmJlY2FmYTAyYzJlYjdjOTRhODQ4ZDNmNDUifQ" + } + ] + }, + { + "id": "claim_ec3446189e904a6b918de2721468b2dd", + "statement": "MCP tool result conversion handles TextContent, ImageContent (base64 or URL), FileContent (base64 or URL), ResourceLink, EmbeddedResource, and raises NotImplementedError for AudioContent. isError=True raises _MCPToolExecutionError (ToolException) with content; other failures propagate.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L120-L181", + "version": "repo-lines-v1:sha256:0e3f074612917c8d40758869d2f765edae41b8e1bcc04573b80a3e98bf795dba:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ5MTMxZmM0ZGQ4YzA5MTUyNzFiMzdjYWI4MDgwNGQ2YmUxZWViZDYyNDE4M2M1NGY2OWNiMjY0YzkyZDA5YzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg3YzIwNWYwZWM5ZTgzNDU0MWViZmEyMThhOTE1OGQxNTNmNThjZTFiM2E0ZjdiMDQ5MWY1YjdiMmVkMjkxYTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNjZWU1YWFhYWY2MWU1MTc5ZWNlM2JlYTJhZTM1MTY4ZTMwNzBmM2QzNjM2YzNmM2ViYWQ1YmZjMjMyZmNjZGQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L89-L102", + "version": "repo-lines-v1:sha256:2f8b8c0b921b595cd94aad7af81eec118ae08d4f16aafa8a040a29b8ab02224b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZkNjVkYmI2NmVhM2IyMmEyNzNlMTlhOGNlNWE4YzRlMzAyNmZmYWE1MDg3YTRlMmM2MzIzNjI0ZTkwNjg3ZDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM0YzYyMGNmZDdkMTc5MjBmYzY3ZTljMmRjN2M3MzU1NTFkYmM4NThiMzdjMTBlNzYzMjNiNzk3OTI4Y2ZmMTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk2ODJhY2VmMWJlMTdjZTI0MTk4YzZmZGQwMjQwMDJhYjk2ODQyZjdhZDJiNGRiZmU2ZWZlNDgxZjQ4MDE2Y2QifQ" + } + ] + }, + { + "id": "claim_5d4f2e62b78b4938af932d769149d16e", + "statement": "_tool_metadata() collects annotations and _meta under mcp.tool namespace and server_info under mcp.server namespace, allowing clients to read destructiveHint and other MCP metadata without hardcoding tool names.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L183-L209", + "version": "repo-lines-v1:sha256:e1314169115084c97d944fa49a783b06187552b721f4c792ce6cd842eb2e7bcc:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYzYWJkNDcxYzM1MmVmZWI3YTE3ODI0ZWZhYmNlOWZhNGZhYWUxZTkyNGYzNTQ3YmY0Y2ZmNTA5ZDRkODU4YjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFmYTc5ZGVlOTNlMzdiZmU2MTVjOGI2Mjk4NWJlNDEyMTIyNmUwOTUzNDU3MDdhZGU5ZmViNmE4ZTc1MTY1YWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBjZmJlZmUyYTE4YzRjMzVlYjc5OTMwNTI0MzFiODVkMjgxZmQ3MjdhNzBkNTI3NTdlZDYxYzhmMTYzNzlhMGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVjMDE1YWI5YzY2YzA2YmIzYTBlMGNlNGQwYWZlMDQyN2I0OWY1YmI1MTRlZTc1NzE0MGUwMzBkZTFkOWYzYzAifQ" + } + ] + }, + { + "id": "claim_61c680e578b640029e6bd3bca910895a", + "statement": "For ClientGroup targets, tool names are prefixed by config key (e.g., weather_forecast, calc_add). as_langchain_tool() resolves the serving member via client.resolve_tool() and routes each call to the correct server.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/multi_server.py#L24-L29", + "version": "repo-lines-v1:sha256:cff3007f0c06aae3726575bf95c7402e922534d3c57e56fd8cac6673f35bea4c:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTA3M2Y4MzdhNDFkNTZhMzM2ZDk3Y2U2ODc5NWFlNTdhMGUwYzkwMTE1NDNhMmJkNzc0ZDkxZmU4OGY1NDg5ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDEyY2EzNDVjY2Y3NWJmOWMwODA2YmNlNjk1YmU4ZGU4MDhiNzk5ODQyNTFhN2E1NGQyMDJjZjYxMDFkZDQ1MSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDUzYzM3NjcyNDRiZGVjN2E4OGViODMyNmEzYTVkMzBiNDA2NmU4MDFmMjM3NDI4NzZjYjE0ZjVjODMzZTYyNyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZmRmNzgzMDg4OTMxMTliYzA3N2Y4MjY4ODgzZTIyOWFkY2MxZjcyYzVmNTgxOWFmOTYyYTI0MjQ4NmY0ZmYwNyJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L190-L192", + "version": "repo-lines-v1:sha256:2523f07b0eea5566e646d6cc27c692e971008ef2968e0e4d4a055acda3366055:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWVlZTU1ZWU4NDMwYTZiMGRlYWU3YWY2YzI4MDhhZTVlYTE2YWZkNjYxMThkNmQzODgwM2RiMGY2NWQ0ZjE4MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjczZWMzYWFmMTQ2MmUzNzI4MzcwMzJmNTQxMTA5MzQwNTU1NTg0OGNlMzk1M2JjOGZmY2FjNjhkNGM4MzFkNCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOGNlOWU1YjEzYzEzZTg1ZjY0NTdjYmQ1MTdjNTU3OGU5NDE3YTE4MjNkMjNhYjgwMDA5ZTNhYTExY2FjOGM5NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMTY3ODE4YjFlYmFmYjliZjI1MGUxMjRiMmRlZTdmNzI0ODdiN2Y3NDU1NWRkZDZmNDI2YzFmNWRhOWRkY2Y5ZiJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L254-L256", + "version": "repo-lines-v1:sha256:ce8b9672906b2065430b916cfc30c7fc92b475ef6857f46e7f1d138c1dcc2d6c:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWJkZTNiZWU0ZmE4NmIxYTE1NWViMzA2OTI1MjI2MDBhZTI0N2Y0ZjhiZTkxZGUxOGUzZTYzODdmMzY1ZDJlNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzY2NGY3MjA0MzE5ZmJmYzhlODNhZDY3YTg1ZWI1MjA2YTBlN2YzYjAxZTUyYTA5NjhmYjM4NGJmNmNjYTExMSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiODYyY2UyMzY4MDQ1NmMwZmM5MzQxYWRkYjk0OTM4ZmZjYWJjZDMyYzliNTcwY2U5OWY3ZDAzOGY5OTlhYjk3NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTZhMWQ5YjE2MmRiZGVmM2M5YjZmNmRkZDhkMDQwN2Q5MGY3NjgxNDRjYjlmYjI5ZmY3NGRkZDlhMTg2YWI3YSJ9" + } + ] + }, + { + "id": "claim_80786903d9ee40a7aff339ae6ccb1852", + "statement": "Transport inference is delegated to FastMCP: in-memory (FastMCP instance), stdio (Path subprocess), HTTP (string URL), and explicit transports all work through the unified MCPAdapter interface.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/README.md#L12-L23", + "version": "repo-lines-v1:sha256:94d68e03e0e8645c7d6af8f79c8e5db2d6b305de4e58b68fd136f0fb722433d1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjMzY3OGUwMTI1MWFjMGMxMzUzYmY0NWE0Njk3OWEyNTgwYWU5NTk2ZDVjNTc0MjlhNmY5OTg2OWM3MmEyZjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY5MzkzMjE3ZGEyMTI3ZmFiNzVjZWNjZjNmYTEyYWQ0NmQyNmQ5ODFjMGJmMjAyYWY1NTA2YWVjOTRjNjVlY2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhiN2U5MTc4ZjI4M2Q4Y2YyMWQ5YTNjZTI4OGUxYjA2NWE0MTEwMjNlZGJiOGE2NTdmMTA3MTc3OGVhNDM3NmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA2NzFlNzM2YmNkZmI5ZTA1ZjZiYTM4MThlOGM3NzQ5M2Q0ZmY0YWY4Yzg3YTY2NDczZWM4ZWJlNDk0YjVhOTEifQ" + }, + { + "resource": "repo://libs/langchain_v1/examples/mcp/transports.py#L27-L48", + "version": "repo-lines-v1:sha256:15ec552dc103a07e4312fd787d1488280d7e8cb8f32d8ade03e58821bab06c03:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgyYzE4ODVhNzNkOGI5ODdlN2YyYmZlYzAyMTQyNTg0Zjc2ZGM1YTkzNTYwZTk3MDY3ZjBiMzg0Y2JiMTZlYmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFkMzMzY2NkNThkMWU5NjY1MjY0OTRkOTc5ZjAxM2Q1M2M4N2FmMjE5MzdiNmU1ZjJhODMwZjU5YzQxNWI3MTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhNzI4ZmFmNzQ4NGNlMThkOTZmOGVkNjc2OTU2YjRjM2I3OTk2YzNiNTlmZTU1Y2FkNTg3OGQyM2NjNDdlZTcifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py#L125-L134", + "version": "repo-lines-v1:sha256:5e27198ac6b82d8d9dd1f60c0baba879b8e89f468800c6990d3a4165ab1dce7b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlMzNjYzBkZDhkMDU2NjRjYTQ5NDc4YWU3OTNjZjdmYTE0NThlZmE5YjM4ZTA1ZGRiNDk5YWIyOWIzZTFmM2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBjZWZkYjRlOTAyYWQzODcxMzI2Y2E4YjZhOGNjNWZkMDkyNDkxZDIyZDhjZDAxZjIyNjk5MDlmODc2YTVmYWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFiOGYwNThmNzdlYjg1NDA5MDYxZDg2YjIzMDg5MzQ2MzBhZTExNzlkMDkwNDdlMTRkMzRkOGNmYTU4ZjU3MWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImEzZTQzMzhmNTIyY2QyN2Y3MWQxNjMwY2I1YzBjNjVhYzUyN2RkYTJkNjE3NWM0MGFlNmJlNzQyNGE0YWEyYmIifQ" + } + ] + }, + { + "id": "claim_6d76b822826c4c93bcc95f685e5d1ebf", + "statement": "Elicitation interrupts only answer ElicitRequest (form or URL). Sampling, roots, and continuation requests raise NotImplementedError. Resumed answers are validated against request keys, actions, and content schema.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L175-L221", + "version": "repo-lines-v1:sha256:8d1053141986ff1138e54a7569c7a4624b5bee43967e612b74d35614c13e65aa:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNkMTFkZDc3ZDIwY2E1M2VmZjM0YjcxMTdhMWFhYTQyMmEyYjhiY2MzYzk4NjdiMGM3MmRiZmEwZTNkNjY3ODgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc2YzI4YjIyYjU2MmRkNzljMGIzNWJlMDg0NWI0NDdkNWUyODg2Nzc2NjczNDdlOWQ3NGQ5NjJhYjQxNWM5OWQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhjYzRhN2QxY2M3OTZhYmEzZTQ2NzIzMzI3ZjUxN2U5ZjgxMTk4MmQyNTBmMDcwMjk4NmU0YjU3NjRhZDk1ODkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdjMWRmNTA4ZjM4YjgxYzI5MjA0ZjFmYTdiZDdkOTM4MjYzMTA1MDA5MTkyN2EyZWY1NWM2MzA5ZTI3N2Q0OWMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L359-L366", + "version": "repo-lines-v1:sha256:891e15538084f7f4057003301c590f2bf82b1fb88a9ff101a26d41e1b3826914:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDQ3YzlmNjUwZmFkOTEwYTRjMjBjZmM2OTE0YzllMGFhNTk5MDM1ODgwZTQ5YWM4MjQ2YmYxZDk4ZTUzY2ExNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNGRjNzc1YTc5NTNjMTE5ZjkzMjUzNmFmODJkMTBmYTdiOTg3YTRmN2FkMTJiZWM4NmVjNGI3NzY1OTVjNzI5YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzA4OThmNzQ4ZDNiOTk3ZWFhOGFlNjMxMjkwMzhjMmUzYWNkY2Y5ZWI1NzU2ZTg0ZWZhMjM0ZjIwYTE1ZWZlOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTBiMDA5ZDBiZGE1NTUxMjhhZGEyZGUwYmExYTNlNDIwOTE0NTM3ZTBiZThjYzk0NWE4ZDJiZGZmZGI4ZjU0YyJ9" + } + ] + }, + { + "id": "claim_01872efc598d43bbb13c355a6c5fcd17", + "statement": "MCPAdapter supports bearer token and OAuth 2.1 authentication via fastmcp.Client auth parameter. OAuth supports dynamic client registration with browser redirect and token exchange. Multi-server configs specify auth per server.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/auth_bearer.py#L43-L58", + "version": "repo-lines-v1:sha256:a642187463c514634c6b791940307a975bc17210f0464f459078384f8936cc86:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA1MzcxZDhiMGU2OWQ2ZGZhMWYwODcxMzhjM2MyNDFkMmVlMGY0NjlkY2Y1NGFiODY5MmQyMGRkNWRjYzY1YjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZmMjU0ZDljMjkzNzE2ZjVkNjM1MGY2OTk1ZWZkZTg3MjhkZGMyYmI2ZTliY2Q4YzQxNjcyMGUxMmNmNGU2NmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhNzI4ZmFmNzQ4NGNlMThkOTZmOGVkNjc2OTU2YjRjM2I3OTk2YzNiNTlmZTU1Y2FkNTg3OGQyM2NjNDdlZTcifQ" + }, + { + "resource": "repo://libs/langchain_v1/examples/mcp/auth_oauth.py#L37-L74", + "version": "repo-lines-v1:sha256:d33b5305621fa2b038ad6f8932501a20c2b6e6aac29345072efd9f514e2589e0:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI4OWRmOTgyNjA5NjBiNjVmMTEyNjNlZGY4YTBiNWY1MDY5NjU4ODFlNDQ2ZDhlNDI3YzMyMzI5NTMyODUyMDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY3ZTNlMzg5MGY3ZWFjMWU2OTc2NWQ5MDhlMWU4OWUzMjJhNjBkYzYxYWIyZDNlZGQzOTIzYTAzNzEwNTMzZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhNzI4ZmFmNzQ4NGNlMThkOTZmOGVkNjc2OTU2YjRjM2I3OTk2YzNiNTlmZTU1Y2FkNTg3OGQyM2NjNDdlZTcifQ" + }, + { + "resource": "repo://libs/langchain_v1/examples/mcp/graph_factory.py#L44-L59", + "version": "repo-lines-v1:sha256:4d19289aaf3fa5d7b6e959cd6d473f01613aca9ee04d7f604f68a0601def8f61:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzOGJkZTExMzU5ODIwYjY2OTNlN2I5NmNjYTEzNWQzMGUzNWMzNzk0ZTIyN2U2ZWJiNDM2NjlhYzQzNDE4NGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM3NDhmNzZlYjA1NDdkYjJjMWVmMDU0ZmE5ZTEyMTYyMGI5Y2ViYzkyOWNlY2JmYmIzMjNmM2RmNWI2YmY3MzEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0YThjMjZkODNjZmU0YzA2ODNjZjBkNTNhOWU0ODQ5MTNhMjRiNmZhY2I4MDI3OTdkMjBlMjRkY2RhMWU1M2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_23605e2a533241bb977a93b7b293c090", + "statement": "Elicitation handler _declare_elicitation_capability() exists only to advertise the capability on modern servers (2026-07-28+). Legacy servers initiate elicitation the old way and raise NotImplementedError, requiring mode='legacy' or a pre-built client.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/elicitation.py#L228-L243", + "version": "repo-lines-v1:sha256:48b8d079df4c2a21567784c48e8a245c96b1e36b85402c3e07806dd0b0301e81:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEzMDQ0YmY2MDNlMTAxMjNhYzBkYmU1MWRhNDdjMTc1Y2YyMmFhZDAxMzE0YTllNDY2MmZjYzk1NDg5NmI4YmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE3OGZhZGUyZTcxZGIwOGJmMzdjNWY5OTQwMzY2OWFkYTk3ZjNiYWFhOGI2MTUwODNiNGI4OGMxMGUzMWE2ZmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFlNGFmYjk1MmU4N2RlNjkwZjEwMWFmZjMwYzE4ZDNlNjgxYzNjMDUzYzVjODc2M2RiYTlhMmRkZmNkZjNjOTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE2ZjU2NmE3MDRmZTRiOWUyMjNjNzRlMzk2NTBjNjE0YjZiZTM2NWEwODBjNmEwOTc3MjkwN2QyYzQ3MmRlZGEifQ" + } + ] + }, + { + "id": "claim_4ed9c12846e448aba04fc0015a9cd61b", + "statement": "Protocol eras (2025-11-25 handshake, 2026-07-28 discovery) are negotiated per connection. A single MCPConfig fleet with mixed eras drops to legacy; separate adapters per server preserve each server's best era.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/protocol_eras.py#L30-L50", + "version": "repo-lines-v1:sha256:b7f51d5ac0943b1555424e9c8a3c9ad78035e348b9da14b35ec46cb8b1a87692:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdmNDIxMmNmZmNkNzMxMzFlZTc4MWRlM2RmY2M2OWMwMWM2YmZlZjY2NGM1OWE0YTMwMGY0MWQ1ZTgzM2FhNGIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNiODkyYzA3NDQ2ODI0YjdmMjVmNWU2MmQxZjc2Njc0YjNlMzc4ZDE0NWYyYjgxMmNhZWRlOWQ0MGUyNWFiMGQifQ" + } + ] + }, + { + "id": "claim_52108e3465454669951d2272538b3103", + "statement": "StructuredTool metadata field carries MCP context: mcp.tool holds annotations and _meta from the tool definition, mcp.server holds server_info (name, version). Clients read metadata to gate destructive tools or apply other policies.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/destructive_interrupt.py#L34-L38", + "version": "repo-lines-v1:sha256:886f2d79d74f941771c5057d3254e360bf8cccc4b267e60c0e78901f8cafb6e5:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNTgxMzdkOGJiZjJhMGZjZGY4NmQ5YTllMWE0MTNkMjMxZTRiYjJkMzk0ZTU5MGM5N2FiYjMzMzQyZWJjNmZiNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZjhiODVjYjJmYzlhYWNhNzQwMTdmZTgzMzllMjMyYjVjZjFlNTU3NmMyNjFhZWQ5ZmE1ZDgzMjUwYTJlYTE3NiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYjM2Y2JkMTZhMTg0ZmJlMWU5Y2ZjOTMyMGUxNzRlYzQ0ZjgwNDc5MTIxMTAyNzFhMmZmZTQwMTc5MGYyNDdjZCJ9" + }, + { + "resource": "repo://libs/langchain_v1/examples/mcp/destructive_interrupt.py#L50-L55", + "version": "repo-lines-v1:sha256:4487ff9fb838c1a738db1fac8bd5aa9591f0f8e94c7ac59476733e1fd8d170d1:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzNhZTY5ZTUwYjM1MjQ4NWRkNzcwNzQ3OGU5ODFmYjk1YzlmNGY1MDUzMWUxZDJjZDNjMThiMGI3NzczMWQxNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMDVhNjBmZTFkZmI2NTZkMjY4ZGQ5YTkyYjIxN2JmYmJiYTUxMTA3M2Q3NTg5NGM4MDI4OGRjM2QyZTM4ODFiOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZWQ3M2QwZTU3ZGMyMTM1NjI3OGMzYTQwNzVhYjMwZWUyOTZkN2RlNTQ1YTJhNTg0Yzc1ZmQzNWU1YjhiMzY1YyJ9" + } + ] + }, + { + "id": "claim_13952fd61c864c79b99d768082ca1346", + "statement": "Graph factory pattern instantiates adapter inside factory for per-request setup (user auth, client creation). Long-lived resources (HTTP pool, response cache, partitioned per user) live outside factory and are reused across runs.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/graph_factory.py#L29-L59", + "version": "repo-lines-v1:sha256:55c0a7c27ba8f1b67e34dbc8571e8960aff6a860fb4ed0e5295054438e1e32e2:eyJzZWxlY3RlZExpbmVDb3VudCI6MzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1ZTEzZDMwOTQ0NDNhMjkyNWMxYzcyYzhkNzMzMDM0OTVmNjg1MjUzYWRmMGQyMDY0MzA4YWNlMmYyNzUyOTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM3NDhmNzZlYjA1NDdkYjJjMWVmMDU0ZmE5ZTEyMTYyMGI5Y2ViYzkyOWNlY2JmYmIzMjNmM2RmNWI2YmY3MzEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFhZDI0NWI0ZGVkYWZjNzk4ZjY2NmViOTRkMTlmNDA4NGYwZjUxYTMwYTU0OTQ0NjE3NzIxMjM5OWQyY2Q5NWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_d8018824efb04f54849f9f85743234f4", + "statement": "Tool result format is content_and_artifact: content blocks (text, image, file) go to model, structured_content goes to separate artifact field. MCP errors become ToolMessage with status=error carrying server message; transport errors raise.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/examples/mcp/tool_errors.py#L1-L41", + "version": "repo-lines-v1:sha256:7f88654ed5e0d53508fbf92475ce8ec1eea9017dd24a75c720a33d70a4ae8fe8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM5MDFlMGNjMWU0MjFlZjhkNDYwZTcwNTQzMmEzYzBlNjY3YmY1N2M5NGM5ZDljZWJlZDk0MGRjMjZmZThhNGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiYTVjMWYxOTA4NjFlODRlZWYyOTc1MjYyNTlkYzRkNjA2M2FlYmQ5YjA5ZDY1NTk4ZmZmNWRjODMzMjJkNmEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNkNDEzNDBjYzIzOWExNTljZTliNjAyMmU5ZjYzMjI4MzJlMmVhZDc2Y2FlOWU0NzU0ZDIxMGViOGZiNmQ1ZTgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L274-L282", + "version": "repo-lines-v1:sha256:4217f28aa4d1d1e13256366f82a3f12ce385c70ea8464a19e62d633e1dba9883:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNmVjYjhlYmNlMzdmOWI1YTg5MTlhMDc4YTgzODhmNTE2YjJkOTFiZThiODdiODA5ZjdlNzQ1OWM3Zjc5ZWQzZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzBjMWYxODhmMTQyZWQ3YTgyNDY4MGIyMDcxNzJjNTUxOWJjNjE0YTQ4ZmRhN2NmMTY3ZWRiOGRkZjc5OTU2YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZWFlOTNjM2JjZjMxYzY5MDdmNjRjODYwZDU2NWE5MzgxY2Y3ZWQwNmFkOGY0YmZjZTIyMTZkZGM2MDMyNTdjYyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTFhNWQwZTdlY2MzNzRhMjE2YzllMDM3NmFkZmM0YTVhZTE5ZDNlNGFhMzA1MGY0MDg0OTM2NGI0MmQ3ZjU5YSJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/mcp/tools.py#L59-L69", + "version": "repo-lines-v1:sha256:f223e0ec29b02335881593acce9d16188ba6f3c334dd20664ac4021f14e1aff5:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgwZTI2MDMyZDFkMjAyM2I1YjQ0OTdmYTQzYzhlOTQwNTE1MzVlMjE4MzM0NzAwMTkxNmVhNjcwN2ViOWE0ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiZDMzZjE4ZGUxMzMzYzdhMGI5YTRjMWQyNjU1YWQ1MmYyZDE4Nzg1OWIxODNmYzZhMmE4MzIyNmNjODNhOWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQxYmYyNTNkYjhlYmJiMzdkODY2YjA2MmJjZjEzNDY2MGM5MWY5MGRlYjkyMjRjNDNjMTNmNWY0NjlkZWYyNzQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxNmZmODcxNDZhZmNjMGQzNjM0MGNjZGNhMGI3YzkxNjk2MzQ5YzllZDJiYzNlZDIyZmI1NmU1ZGI1MjdlMTQifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/messages.json b/openwiki/.claims/messages.json new file mode 100644 index 0000000000..cd29c2e2cb --- /dev/null +++ b/openwiki/.claims/messages.json @@ -0,0 +1,212 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:64e569ccd662acb76519938c6e8867f0e82285557634fc1a08205e1d12e2dcee", + "claims": [ + { + "id": "claim_cd426b48afb54e54a30a82b280ab266d", + "statement": "BaseMessage is the abstract base for all LangChain message types and provides a unified container for conversational inputs/outputs with fields for content (string or list of content blocks), type discrimination, additional provider metadata, and response metadata.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L93-L180", + "version": "repo-lines-v1:sha256:e6be22558dc63e5340eb68ee9c6fd5e038ce8e73a744032231edf160bf532c1f:eyJzZWxlY3RlZExpbmVDb3VudCI6ODgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YWFlYjBkNzJlNzU4NDhiYWVmMDliMGJiMzU0MDY4MjJhZmFmYTQ4YzEwMWU2NDc5OTE4Yjg3YWFiOTU2YjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyOGVlZTRhYzUwYzgwY2I3ZTU5ZGUxNmEwZWM4MjllZGViYjRmNTJhZDkzMjk2NzNmN2JiMTA4NDBjODlkMTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRiMTc0NWI5NTA3OTM2ZTRiMDAxNGUxZDg2ODJjYTEyNzZkZWM3NTVkMjgzNDI0MTE2MTM1ZDcyNzVhMjVjOTEifQ" + } + ] + }, + { + "id": "claim_f148a9f899f74be1957cb21dd4b44c05", + "statement": "Content blocks are provider-agnostic TypedDict objects representing distinct types of message content (text, images, audio, video, tool calls, reasoning). This unified representation allows applications to work with multimodal messages portably, with block translators converting to provider-specific formats at invocation time.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L1-L50", + "version": "repo-lines-v1:sha256:3010069d9da4470579522ae5c22b1cd7c3b8e2a1881470fa9a867986f25ba595:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAwMDU0ZjZiZmQwY2ZjMzQzZDgwOTdhMDE2YjU3NGY1MGE2MTA4YzEyMzE4Mzg2NGMwNGQ1NjdmYjRlMmM4N2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIxYTEyODJiMjZhZDE2ZjczNWExODdiMjFhNDNhMmU0YjBmMGNhNWU1OWI1YjJiM2UzMzBiY2Y1NjU4MWFiYWQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg0MGQyZjI2ZTM5M2NhZmY4MzFiNGI5Zjc0ZmVmMjBmMTJjODdkZDY5YjA0ZmZkNzY4YTNmNTZlZDY1ZDJiMzkifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L207-L854", + "version": "repo-lines-v1:sha256:443c2a61daa8ec2c1a04b6001261004b03052b53d5f8467e556eb5010dd71d3f:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQ4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5Mzc0NWI4ZTkxYmJjODU4NWNkOGNlNWJlZTJmNzNjYTZjNGZmMmIzYzJjYWE4NjNlNmYyY2VhM2YxMTczZjJlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI1ZWNmZTZhMjUyYWQ1NjZhYjNjODdlMDJmYjQxYWEwMDJmOTQ5NTRiMTdiMjAwN2M0NjNkM2I4MmRiMTRhMTVjIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIzY2RhOGM3MDZmMWMzOTE3NDRmZjJjOTU1YTk3NTNiOWIxZmQ2YmJlOGIyOTI5ZDM3MTE4ZmMzY2QyMmM1MTk1In0" + } + ] + }, + { + "id": "claim_a01e83ca2e4a422b99842c4831a6783f", + "statement": "LangChain defines a hierarchy of message types: HumanMessage for user input, AIMessage for model output with specialized tool_calls and usage_metadata fields, SystemMessage for behavior priming, and ToolMessage for tool invocation results. Each type has a chunk variant (e.g., HumanMessageChunk) for streaming support.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L160-L184", + "version": "repo-lines-v1:sha256:ab544c9f30f60ef9431f7eea9ecfbf638910dead0cd16285b7937e06ed8d1aed:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhZGMyYWFlNjZjOGFhMTliNDE4MjA1ODFlYzg4MmY2NWRmOTljMzE3MWE4M2Q2ZDk3MTQyOTlmYjAyM2I5NzMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ5OTc4YjNmODQ0MDBhNThiOWRiNzkwM2ZjNGY2MWY2NGQ0NmFhNTFhNDAxMTEwYjM1NTYzNTJkMmQzZDk1ZWQifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/human.py#L9-L70", + "version": "repo-lines-v1:sha256:bdb487591d3c811ed2ed6efd88f5f42f6316da72bc4de025cc33c082f1f09e20:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhNWNkNmZmM2Q1NTA3NGU1NTQzODlhY2Q0ODc5MDJhNjljYTZiNzdmM2RjZWI3MDMwM2E4ZmYxMDBhMzVhMzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIwOTU2ZDVlYTkxODg1ZDk5NmQ3OGEwOTJiNzY5NzczYTFiYTA1YTY5MGM5NWZmNTgxNjFlNWMyNzdmZGM4ZmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQyY2QzODQ3NzZjM2FlM2U4ZGZmOTc0OTlkMmRhNmJmYTlkYzY1MGI5OGY1NmM5M2U0YzM1NTNmNGFiZjY2M2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/system.py#L9-L70", + "version": "repo-lines-v1:sha256:2b2007d9a483f27f7452ef5f9d9c99e7a11abf803869112f2c2a80a2ef249c92:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU4MTk2NTMxMjU3ZTg0ZGE3NTM2YTAzZDgxZGE4YmNkZjU1Y2QzMzFiZTFlMTJhYzc0ZTE1MTAyNDVjNGIzN2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIwOTU2ZDVlYTkxODg1ZDk5NmQ3OGEwOTJiNzY5NzczYTFiYTA1YTY5MGM5NWZmNTgxNjFlNWMyNzdmZGM4ZmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQyY2QzODQ3NzZjM2FlM2U4ZGZmOTc0OTlkMmRhNmJmYTlkYzY1MGI5OGY1NmM5M2U0YzM1NTNmNGFiZjY2M2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/tool.py#L26-L90", + "version": "repo-lines-v1:sha256:cda7d78ed08c67de83349159d40f0672f0c94fc657af8300d39d5c5918b16b87:eyJzZWxlY3RlZExpbmVDb3VudCI6NjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVhYWYzYjdkMDU2Njk3ZDEyZDRhYWZiNzU5ODY1M2IwZTViNThiNDgxMGZiMGViNTMxMDc1N2VjMDZlNzQwNDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1NWQzZjRmNmJlNzRjOTY4MjViZmUxZTVkZGZjMTk2MjI1ZWRlZTBiM2FkZDU4YzYzMjhjMTQ4YWNiNmUyMTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNhMzYyYjQwNmMzNGU4N2U3ZTQyMTMyNjQxZDdjYmIyYmJkMzk4YWJmYWI3YTc1ZjVjMzdiMDc1YmVlZmExNGQifQ" + } + ] + }, + { + "id": "claim_815e42c5a789483ca9dc71615ac64143", + "statement": "AIMessage contains three tool-related fields: tool_calls (list of structured ToolCall dicts), invalid_tool_calls (ToolCall dicts that failed parsing), and usage_metadata (standardized token counts with input/output breakdown). These fields are distinct from additional_kwargs and enable structured tool invocation tracking.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L160-L181", + "version": "repo-lines-v1:sha256:d9285d66b3ff292b940ef24fc07fc28bfb31977b9abf702027d23e40b3afa869:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhZGMyYWFlNjZjOGFhMTliNDE4MjA1ODFlYzg4MmY2NWRmOTljMzE3MWE4M2Q2ZDk3MTQyOTlmYjAyM2I5NzMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllMGIyNzcyM2JkODk1OGVmM2NlMjc1ZmQ4YjU5Mzk2ZGIzYWJiOWU0ZjIwMjEwYzliMTIyYTk5OTYzMWJmYjEifQ" + } + ] + }, + { + "id": "claim_0c6ce54d65b741f280afe9c0c8004159", + "statement": "AIMessageChunk represents streamed model output and holds tool_call_chunks instead of complete tool_calls. When chunks are merged with +, they accumulate content, combine tool_call_chunks by index, aggregate token usage, and on the final chunk (chunk_position='last') parse accumulated tool_call_chunks into complete ToolCall objects.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L418-L506", + "version": "repo-lines-v1:sha256:0b2906c517c8e8957ce1c79ed485a05797e12dbb257d0e6fb397abc83b51bcb6:eyJzZWxlY3RlZExpbmVDb3VudCI6ODksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkZDk1M2U3NmU3YjI4MTJlMjA0ZTA0NTQxNjJjNjNlZmZjMTgwNGQ1MjZkOWExYzdlODE4OGRmOTNiMzM5MGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNhY2ExZGNhNzcwZDMzOTU2MDc1NGI3ZmNmNDlhZTNkMjYxZjljNjc2MGU3YjU3YjIyYWY2MTMxM2FjZGVmNjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNlMTI3MDk5NGJmYzY2MjFhYTE1YzEyZjEyODcwYTVlYmI0MGE2YjhmNTBjZjE5NTE2MGRkNGNhMjA5NzhiMTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE4MjYwY2IzYTUwNmJlYTViZGFjZTliYjg3NDE1NWU4YjgxNTI2MmZmMzY5ZWQ3N2Q3OWU3ODFkZTViZDVlNmMifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L652-L700", + "version": "repo-lines-v1:sha256:b8fb7ef2b65300fbc6354fd89ca5416e62f0c1d483cfca20bd751d90eadf8768:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQyMWI0MzJhMzNkOGMxYTM3ODMyYjUyZmY4OWMwY2M1MmUyZjE0NTRkMjE2OTQ0YjUzYjUyOWU1M2ZjYzQ3OTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEwZjM4YzNlMDdlZmQ2NWI4NTM3NmEwMDY1YzRhNDM2YzY0MjczY2ZmMTM5NGM0YzMzMzEyMWZjYzIyN2QzZTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgwMDQzNjdjZWJlMDRkMzQxNDNmZGM1OGY0OGU1NWQ3MWM3N2ZiZTU1OWRjNjJiZTE0N2FjODA1MzY5NmRkMGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE1MGY3ODAzMzIwMGI0ZGIxMDlhMmRjNjBjNTU3ZWU5ZjllMmYxOGIyNDZjN2E5Zjg3OTM1N2YyY2UyMmFhMmIifQ" + } + ] + }, + { + "id": "claim_4641a9038e4840fe935f1c5b559e782a", + "statement": "ToolMessage encodes the result of a tool invocation with required fields tool_call_id (linking to the AIMessage.tool_calls[].id that requested it), content (tool output), and status (success/error). An optional artifact field holds full tool output not sent to the model.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/tool.py#L26-L90", + "version": "repo-lines-v1:sha256:cda7d78ed08c67de83349159d40f0672f0c94fc657af8300d39d5c5918b16b87:eyJzZWxlY3RlZExpbmVDb3VudCI6NjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVhYWYzYjdkMDU2Njk3ZDEyZDRhYWZiNzU5ODY1M2IwZTViNThiNDgxMGZiMGViNTMxMDc1N2VjMDZlNzQwNDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1NWQzZjRmNmJlNzRjOTY4MjViZmUxZTVkZGZjMTk2MjI1ZWRlZTBiM2FkZDU4YzYzMjhjMTQ4YWNiNmUyMTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNhMzYyYjQwNmMzNGU4N2U3ZTQyMTMyNjQxZDdjYmIyYmJkMzk4YWJmYWI3YTc1ZjVjMzdiMDc1YmVlZmExNGQifQ" + } + ] + }, + { + "id": "claim_84c0cebf071043cb9da5136a5f4f987a", + "statement": "The content_blocks property on BaseMessage normalizes message content to a typed list of standard content block dicts. For AIMessage, it checks response_metadata['model_provider'] and uses the registered provider translator if available, falling back to best-effort parsing. It also appends tool_calls as content blocks and extracts reasoning from additional_kwargs.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L242-L304", + "version": "repo-lines-v1:sha256:208010ce60bbc0d99fa6bc3bb69c71f1f0a49dcb80add7e5132cfd51d79ce712:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZDAwY2YyNzA5NjRkNDRhMTNkZmFjODc1MmIwOWZjYzQzNWIwZjE5YjMxNmFiY2Y4MWE3YWE2NGJiMTIyZDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNhY2ExZGNhNzcwZDMzOTU2MDc1NGI3ZmNmNDlhZTNkMjYxZjljNjc2MGU3YjU3YjIyYWY2MTMxM2FjZGVmNjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ2MzcyYjViNGM2ZWYwZWFlMDc5MDJjMWJmMzU4MjkwMzBiYjM4NjU0ZTQwMzczMjdhNmU0YTBmMDNlY2JjMDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVmOWU4NTZmNTQxY2Y0MzdlNjI4N2JhMTRkMDg3Yjk5MDg4ZmMzNDU5MmM5YzUzYzdlYjIzOTBjYzRiNWMyMTAifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L199-L260", + "version": "repo-lines-v1:sha256:bf8db16b90843709dbd2272712d757239bd36487588c807063fdd67c129b8194:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZDAwY2YyNzA5NjRkNDRhMTNkZmFjODc1MmIwOWZjYzQzNWIwZjE5YjMxNmFiY2Y4MWE3YWE2NGJiMTIyZDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNhY2ExZGNhNzcwZDMzOTU2MDc1NGI3ZmNmNDlhZTNkMjYxZjljNjc2MGU3YjU3YjIyYWY2MTMxM2FjZGVmNjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZjYjZmY2ExODZlMjBhZjQwZDlhNmYzMWI0YmExY2YyNWNiYjM3OTk3OGVkZTRiMTkzZWRmNDQ3OTRmM2UxZmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhZGI1MWJhZjkxMGUyNmM1Njk1OWI3OWFlYWU3N2QzZTI4NzgyZGE0NGNhM2FmNGMxOTI5YWE0NjlkNTY5ZDgifQ" + } + ] + }, + { + "id": "claim_ba1582683e094552aca6d27bf9dfd047", + "statement": "Block translators are provider-specific converter functions registered in PROVIDER_TRANSLATORS. They convert between standard LangChain content blocks and provider-specific formats. Each provider module (openai.py, anthropic.py, bedrock_converse.py, google_genai.py, etc.) registers translate_content and translate_content_chunk functions that are invoked when accessing content_blocks if the provider is set.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/block_translators/__init__.py#L1-L112", + "version": "repo-lines-v1:sha256:a30ffde00a2a75c01989e165a633bbe65f997270705f39d3321aeaaee5eff20c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIzYjQxNTA0NzA2NzdiODlmYTNmYTkzZmFhNWQ1NTYzYjQxZDE4ODM1ZDMwMzQxMjM5NTZjMDVlYjljNTUzNzhkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIxODQ5OTk1NzkzNmVlZjAyNTczNDgyMGRmNGQ4MTA2ZDM5YTIxMDU5MTdjZDI3MTc3NTYxZWU5OGQ1N2FlNjliIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MCwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0" + }, + { + "resource": "repo://libs/core/langchain_core/messages/block_translators/anthropic.py#L1-L150", + "version": "repo-lines-v1:sha256:0ac55cc08f4c892b3e967c3fc1e1f6a6693028ea80185d4e9c28c802ffb4c800:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyZjUzMzQzNTVmYWE3MWVhMDdjMzExMzEwYTI2NWMzMjA4NjA0OGEzYjIyYTJkODAzN2I2YjM3M2IwMTc4YTA2IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI4ZGIzMzljMTdkNGNjNTRlNzBjNGZjZmVjYjYxMWJiOWU3ZDM2M2YyNWIxOGUwOTIzYTFkMzA2Y2M5NTkyNGU1IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiODNlZTBiOWFmYzBmZjBjYWQ5MTBmMjcwODM3MzAwN2M0ZGUxNDAxOWZhZWE1MTM2MmZmZjJhMDQ0Zjk5MDhiIn0" + }, + { + "resource": "repo://libs/core/langchain_core/messages/block_translators/openai.py#L1-L80", + "version": "repo-lines-v1:sha256:12744af16ab250c9147d46a230ca0620e52c9082f346314510133235b563f09e:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2N2FhNDdmZjgyZmNhYTIwZjBiNzU0NTkzYzNhNTE4ZWY0Y2U2ZDNlNGFkNzkwNzI3NmVmNWY5NTIzZGQ2ZTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQyMmVmZTdmMTBlZTI2ZTE0MWQ2MzAxNWVlOTBhNTZjZDViMTE0YTc3NjMxYzA4ZTQyZTI1MGEwZGUwMTg3NGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE1N2Y5MmVjMjA5Nzg3NWM2ZGE5NzYyOTQyMTExYjg0ZWEzMjU5ODhmYTM1YTVkNDAwMTIwYTljYzZhNjNiNmMifQ" + } + ] + }, + { + "id": "claim_635116bd820a407aa05a586c17304620", + "statement": "Standard block types include TextContentBlock, ReasoningContentBlock (for chain-of-thought), ToolCall and ToolCallChunk (for tool invocation), InvalidToolCall (parsing failures), and multimodal types (ImageContentBlock, AudioContentBlock, VideoContentBlock, FileContentBlock, PlainTextContentBlock). NonStandardContentBlock holds provider-specific content that doesn't map to standard types.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L207-L878", + "version": "repo-lines-v1:sha256:740a434d1c199ebbb4b227c49ac2a4a2c619ed407e26ed789c9b19e09c965c83:eyJzZWxlY3RlZExpbmVDb3VudCI6NjcyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5Mzc0NWI4ZTkxYmJjODU4NWNkOGNlNWJlZTJmNzNjYTZjNGZmMmIzYzJjYWE4NjNlNmYyY2VhM2YxMTczZjJlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiZDM1MzExNGEwNjdkMjBkZTYzYmU5MWRjNGFlM2ViYzRhZDgxNjcxODJjOWU2YWVhMjdmMDNiMGRjNTA5ZDc0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI1ZWNmZTZhMjUyYWQ1NjZhYjNjODdlMDJmYjQxYWEwMDJmOTQ5NTRiMTdiMjAwN2M0NjNkM2I4MmRiMTRhMTVjIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJkM2JkYjY2NDJhZjc1ZGNkZGFjMjJiNjM3ZmM2MmI4MTM0MGJjZDk2ODliOWZmZGUyNzA1ZDhkOWI2ZTJmM2EwIn0" + } + ] + }, + { + "id": "claim_7fd69a5aeb834b6f99b99fe382e5901c", + "statement": "The text property on BaseMessage returns a TextAccessor (string subclass) that supports both property access (message.text) and legacy method call (message.text()). It extracts and concatenates all text-type content blocks, ignoring non-text blocks like images and audio.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L262-L292", + "version": "repo-lines-v1:sha256:7c4b10952d1b4a85cb35cf35560bc29b135650323bdfd0aed62c470713020c6e:eyJzZWxlY3RlZExpbmVDb3VudCI6MzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZDAwY2YyNzA5NjRkNDRhMTNkZmFjODc1MmIwOWZjYzQzNWIwZjE5YjMxNmFiY2Y4MWE3YWE2NGJiMTIyZDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdmMjE4NDVhOTYyYzI1YmRlNDg4YWY0OTFmYTk0NDljMTUwMmIwNGI3NWJhMzA0YTk3ZTM1N2NjY2IxYTdhMDEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE3NTUwNDU2NmFiM2UyNjhiNWI2ZWNmOTE2NGQ4OTc0NDE0OWViNDZlY2RkZmIzMTlkYzYyNjU4OGVmMjlhODAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFkM2FmYzc4MjQ2N2U4MmZmZDU3M2E2M2YzNWVkMjk1NmFjYWNhOWZjOWFjMmFlMGNiZGRmMTk3ODdjNWZiOWIifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L47-L92", + "version": "repo-lines-v1:sha256:b401630aed6bf8d2017f25f4e9b7bfc836afd5620b0f45a7e547e68ff34719db:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2ZGM5ZmFlMDFjODdkN2M2NTg4Yzg3NmNhM2NiODZjNzAxMDQ3NmM2ZWY5YWJiYzVmZTcyNzA0Y2M0YTc5OGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MDE0OWUwNjlhZDY3ZWNjZjQ3MzY0NmZmMWM3NTAwNTBkMzI5ZDExMDAxOGRjNTc2Njg3OGVhZmExZDYwN2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRjZWNiYTFlYzMyMWNhNDg2MzNiMzEyZmEyNjIzYzQxODllZmE4MTRlNThjZTE2MjEzNDk4MjM3MjQ1MmFjZDcifQ" + } + ] + }, + { + "id": "claim_10200f8910bb4ade817078fcb739993c", + "statement": "The utils module provides get_buffer_string (for converting messages to strings in prefix or XML format), filter_messages, trim_messages, and merge_message_runs for message manipulation. It also defines the AnyMessage discriminated union type for Pydantic deserialization using the type field.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/utils.py#L287-L410", + "version": "repo-lines-v1:sha256:318f3fd27bc9861f4c8972c4216c4e73c2b39eb78be1b3d529364beb60a5c985:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI2MTI4NmQ1ODU1ZjE2OWM4MGU3NWMwYzg0Yjk0MmViOTg3YzkwMTgyYzZmMWI5Nzg3NTViMzQ0YzQzMzJhOGQzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiNzU1YzY0OGYzNzdiNTRmODg5NWMxZTBjMDc2NzhiNGEwNWZjNTQ2YWUxNWZiMjY5YWU1NmRkYjc0MjllOWY3IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzNGM0OWEwNzMzOTE4NTk0YjFjMTIxZTBlOWZlNmQ5OTE2ODY1ZGQ5NTIwMjg2MDkxNmQ1MmMwZGYyZDEwMWY1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNzg3MDE1MDBiYWE3Njk5MmUwZWZjNTdkNzE5ZGMzMWVjMzNkYmE4OGE3NjA3MjhmOGUxMjQxYjA3ZDZkYzUzIn0" + }, + { + "resource": "repo://libs/core/langchain_core/messages/utils.py#L86-L100", + "version": "repo-lines-v1:sha256:07088aa99c3ae99a9477f43178e329f43357fd6b0e43edcbce9991102a94a595:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBkY2NmOTIxMmMwZjFkMDFlNmY1MjM5Y2ZhOGMzMmQ2MTQwYTk2OTg1YzhiY2I2ZjA1NzY0NTQ0Zjg1Mzg0ZDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5NDE2ZTA5Zjc4YmEzMTZjMDMyNTY4MjEzYjg1NWVlNzU2ZTQxNTY1ZDkxOGYwOTUxOWM4MWZkNjIwZjQwNjAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MjIzMTA2ZmQ4NzQyYjcwZWEyMzNmOWRjMzU5ZjRiMDJiYmIwNjliNzI4M2VlZjc2MjQ5OTUxNmJkN2I5YzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUxOGViNDQ5NDI3NmM3ZmUxOWYwMzZkN2I1ZWE0MzRhOWI1ZjQ4NTgyNjEyZjZmZTI0NDA5MjAxMjZlZGYyZTQifQ" + } + ] + }, + { + "id": "claim_2567a2ede6c84547b9095834d2e2e3c3", + "statement": "Each standard content block includes an optional extras field allowing provider-specific metadata (e.g., Google's thought_signature, Anthropic's cache_control) without breaking the standard structure. This enables supporting emerging provider capabilities while maintaining type safety.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L243-L244", + "version": "repo-lines-v1:sha256:dc1445272c3ba8b119551e1bdba790941885eafdb85f9e95e9a4b0b21602c71a:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYmQzZjRiNTk2MTMzMWJjYzExZGUzNmQxODQzZDhiMWRlM2Q3YTA5M2M3ZTRhNzVkY2YzMTk5NDZlMzhkMTMwNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDk4ODM2MzBkOWJhMjQ0NTUyNTFhMzkwZjM4Y2U4MDY1ZjBjZWFiYmQ4NzY2YjFlM2I4ZjFjOTk4ZmI0NmU4MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYmRjMjY3MDI4MGFkMjQ4ZTg5MzNiMjgxYzQ4MGY0YTAxNmIyZGNlODc5OGVkZGMyZGM3Yzc3MDQ4NzkxMDZiNCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNGVlMjA1NWZhNzhkYzc4MzJjYTgzODkzYjVmNjY1ZjU2OWZiYjM3ZTA5NmZmZDY0NTNmMDc3Y2M0YTZjNGJjYyJ9" + }, + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L545-L546", + "version": "repo-lines-v1:sha256:3c907e1723cf91ebd0b5a2887a277bc826cca3578236092a281018e6001e3ea5:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYmQzZjRiNTk2MTMzMWJjYzExZGUzNmQxODQzZDhiMWRlM2Q3YTA5M2M3ZTRhNzVkY2YzMTk5NDZlMzhkMTMwNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTNhZWM2MzFlMjcwMWQ2YjkxNmMwNWVlMzkyNWI3NmFjZDI4ZWViZmY0ZjE3Y2IzYzNkOTZiNzc3MjgwNmUyMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZmJiNWUyOTI1MmY2MjgzMTUxMTEzOTQwZDhlMGRlNzBjOGM2MjhmM2RjYTgzYzMxMTE4ODAzZjFmY2FkNjc4ZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTkyNTFmNjAwNTUxMDdlZjc0MTVlZTE4ZjRhMDhhMmZjMzA2NTRiMjMxNDkyMzE4MjMzYTQyM2I1NzUxYjU3YyJ9" + } + ] + }, + { + "id": "claim_6402e942f26a464ba36f378970b8fa75", + "statement": "Block translators handle backward compatibility with LangChain v0 blocks that use source_type-based format (e.g., {'type': 'image', 'source_type': 'url', 'url': '...'}). These are recognized and parsed by _convert_v0_multimodal_input_to_v1() as part of the content_blocks normalization pipeline.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L241-L260", + "version": "repo-lines-v1:sha256:5c001e3bb7f850ef81737ca8a0d2c68119528177552e18d3943566223a76c08a:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRlMWVkNWJjMjBiMzEwZjhmNzE0MzM0Y2UwMjk0NDcyYzQwMDhkYmJmZjRjMDhkYTEzNWU3ZjM3ZGEyNjcwN2YiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNhY2ExZGNhNzcwZDMzOTU2MDc1NGI3ZmNmNDlhZTNkMjYxZjljNjc2MGU3YjU3YjIyYWY2MTMxM2FjZGVmNjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdhMzcxY2ZiYWE1NDk0YjdlYmM3ZTcwNzhiYmIxOWYxOTRmNDJkMTI5MjEyM2NlMWNiYTM3ZjQ3OWFiZTIwMTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhZGI1MWJhZjkxMGUyNmM1Njk1OWI3OWFlYWU3N2QzZTI4NzgyZGE0NGNhM2FmNGMxOTI5YWE0NjlkNTY5ZDgifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/block_translators/langchain_v0.py", + "version": "repo-file-v1:sha256:6885eec50990ce1bad12062313d0025ec6699dfe496234d897e8dd7f55a15d98" + } + ] + }, + { + "id": "claim_d7b24c96a7074e02b52f395917200ee6", + "statement": "ServerToolCall, ServerToolCallChunk, and ServerToolResult block types enable asynchronous tool execution. Models emit these blocks to request server-side execution (code execution, web search) without requiring local handler code, with the result encoded in a ServerToolResult block.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/content.py#L372-L454", + "version": "repo-lines-v1:sha256:0d0861d62a7c7f4e07a9ccf9f9c3035c96790465a51dffd49ee58f34d6a93b70:eyJzZWxlY3RlZExpbmVDb3VudCI6ODMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhNTYzNzhjZDkyY2YzMWYyYjU1NjllNjk3NTZjMWIzNDFkOGY2N2JlYTc0NjBlOTIwOTkzNWVmNDEzZTgyNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk3NjdiM2JhMDUyYmEzOGFlMzIxZTNjZmQ3Y2FmY2M0YTU5NzM5ZjNiMTc5ZTE4NDQ5M2I0YmM4MmEyNTI1NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQyMGQ1YzYzNTZhMmRiZjlkOTA2MTc1YzEwNzFhM2VhYzlhZWM3YjFjZGE2ZjQ0NGE1YWYxZTBlNGUzM2FhZWIifQ" + } + ] + }, + { + "id": "claim_7c05b7fc6059477398a20b0f76e73fd7", + "statement": "ReasoningContentBlock represents chain-of-thought reasoning from advanced models. The _extract_reasoning_from_additional_kwargs function extracts reasoning from additional_kwargs['reasoning_content'] (used by Ollama, DeepSeek, XAI, Groq) and wraps it as a reasoning block, which is inserted at the start of the content_blocks list.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L295-L302", + "version": "repo-lines-v1:sha256:a92a7043bc51916ee76d1975d788e8453722c893dd9c6c435d7bbca5541bfc6e:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzc2OGM1YjM4NDBmMDliMjllM2RiNTQwNzU2MjU5NDljZWQwOTI4NzMwOWNjZTMyZDdlMDc0ZDhkY2FhMDgzYyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTZlYjExNWJkMDFhM2Y2MmVmM2Q4OTYzM2RmZWZiMDAwZTEyYmYxYzk3ZGQ5NDExZTQ5MDA0OWJjNmQxZDU1ZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzdmNTEzYjFhOGEwNGE5YTE2OWY4M2ZlMjdkYTZhYTE1ZWMwNmNmNDM4NzQ1YzUwYmQ1NzVjOWQ5NGNlZjQ5NyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiM2NhNWZlM2M5Y2E2ZjQyZDVhYTUxZTg3NjY3YWU1MzcxNTM2ZjFiZDE4ODdkMDM3YzdjYWU3ODI5N2YwNTI4ZCJ9" + }, + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L24-L44", + "version": "repo-lines-v1:sha256:b90fb8e1da0bb50daaf48100c335beb383b72809f18f937c98344cedfa979e84:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM1NTlmNzNiODM3NTkyNTk2OGYwMjY4ZjI2NWEwZGU4N2MzNGYyMTZkNjhjY2U0NDIzYTM5MmQ1NDgwZWZjYzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTYwMGE3M2QyZTAyZjM1MmI5MGZlZjFkOGQ2MzgxMWJhNGE4ZWUwYzFiZWFhN2FlNDY1OGYzNmFhMmUxOTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFiZDgwMTlkOWQwOTMyNjE0NjlkYjQ0MjQ4MGEyMjRiMGI0NWNiMjhlMDRkNGZiMGQwZThkZDNiNjIyZmRmMWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg3Y2RjNDg2MmVmNWIxYWFkZWQzZTc2ZDIzNWQyZDkzZDhhZTZiOWJhOGMyYmE0ODA5ZTFjNGJiYTVmYThmNDQifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/middleware.json b/openwiki/.claims/middleware.json new file mode 100644 index 0000000000..d0d3bb05eb --- /dev/null +++ b/openwiki/.claims/middleware.json @@ -0,0 +1,204 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:edd3a6070069752c8ce3d79e8b4fb6c0e58c7fa04f5bf7b55223db4d14b269a5", + "claims": [ + { + "id": "claim_6ce968382a514edb90e74830ccdebef7", + "statement": "Middleware registered first in the list becomes the outermost layer in the composition stack; in wrap_model_call and wrap_tool_call interception, outermost middleware receives control first and last, establishing a left-to-right composition where M1 wraps M2 which wraps M3.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L263-L352", + "version": "repo-lines-v1:sha256:272c0933c5414b8cecc2e64643c365fa65e76088a91d385cdab443f37076f035:eyJzZWxlY3RlZExpbmVDb3VudCI6OTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1Y2IwNmM0MzdmOTk3ODhiZDVjOGM4MDY5MjU0MTI5YmVlN2ExNGYzMzc2ZTFjZWY2MTczYWUyMGE3Y2YyZjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViZjVlNjBjNjUyNDc1YjVjNzhmMzdlYmI2OTJjNWIxM2NhZDMwMjI4NmM3MDFlYzBhZTAxODVhMWZiMzVjZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI4YjE2YTdhZjFkZDFlYzI4OTY3MWRhMWU5MmExY2UzN2I4MTA0YWFlYjZlYjU3MzY1NmU4NWVhMzU0YTNhY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU5YTcwM2FiYzE5MWZjZWUxOWIwMzM5NTc3MjEzZWQ2OTMyMGRlOTFmZWMwNDdjZjAzYTY0NTUyYzNiYTY1NjQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L503-L576", + "version": "repo-lines-v1:sha256:61e533e2f31a5403230d6d1a73ab426b53143ebeafc173c4080fe3a9ad4129cc:eyJzZWxlY3RlZExpbmVDb3VudCI6NzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyNzFhMGExMzM4YjZlODZjZGFiNjVjNjliODU0NzgxNzIxNWJkYjA0M2MxMDY3ZGM0MTU4MmUwMjE1ZWVmZTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmMTYyMTFiOGZmOTg3NDZjYjdjYjVkNmQ0NWU0NTEzODJjZmYxNDU2NzA2YzZiZGMzMGFjMzhkNTY2YjA5ZWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY1YWRjZThmZDE0OWI4Y2E5NGYwMmQ1OTEwMzA5MWRhNTMxNTkxMDRiMTNhZTlhZjE5OGUxY2YyYzU3MDhhZjQifQ" + } + ] + }, + { + "id": "claim_09073857a7004da38046987c8af77c7e", + "statement": "AgentMiddleware defines optional lifecycle hooks: before_agent, after_agent, before_model, after_model, wrap_model_call, wrap_tool_call (and async variants); each middleware can implement any subset, with unimplemented methods raising NotImplementedError if called in the wrong execution path (sync vs async).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L385-L824", + "version": "repo-lines-v1:sha256:1bca7c0dfb73b3e58a3b17221e416899e34920d00d8f54445e817652db30b813:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkYTZiYjZmZDM3N2VmMGEwZDlmMDZkZmYyNGNjYjNhMmI1NzY1ZTA5MDY5NTExNmU4MzBjOTAzYTdjMmQ3MmMwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI2NmJhYThhZGNkYmI0M2JiMTdiNzU1ZTE4MDgxNGY5MzM2NjEyYThkMTEwMTdmMmY1MTZlMWUyYzIzNTExYzQyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiZTRkODFjZjNiMjA5ZDc2YTUwYTNlMDZkNzg5ZjI2OGVkMjA4MDcwMmQwZjQ0MGZiOGNmMDc3Zjc2NTMxMmFlIn0" + } + ] + }, + { + "id": "claim_65c8d1db27b043c080d964a62daf2daa", + "statement": "wrap_model_call receives a ModelRequest and handler callback; handler executes the model call and returns ModelResponse; middleware can call handler zero or more times for retry, caching, or fallback logic, or short-circuit with a custom result; returns ModelResponse, AIMessage (auto-wrapped to ModelResponse), or ExtendedModelResponse (with optional Command for state updates).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L290-L325", + "version": "repo-lines-v1:sha256:3c30d36312d3543408d371911614fc15118eaf34373c8d25f6d6fa64e1cb60fe:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyMTE2Mjk0ZDU5M2IzNjY3MjYwZGVmNDViNTQ2ZDM2ZjkyMGQ3Y2Y5ODU3YWRjODE5NTQyZjBkZDJlMWZlZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZjMWYwMmZkN2VjMWQ0NjJjMGNiODE4Yjk0OGZmZTRmNzE1YjJjMmM0MzI2MzBjMjMyZGZlMzRkNDI3NDNmYTkifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L503-L585", + "version": "repo-lines-v1:sha256:e2e85f065ac582ee3ff2cb0d26be437d93267a1340f230c025f69dbd634d5ef3:eyJzZWxlY3RlZExpbmVDb3VudCI6ODMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyNzFhMGExMzM4YjZlODZjZGFiNjVjNjliODU0NzgxNzIxNWJkYjA0M2MxMDY3ZGM0MTU4MmUwMjE1ZWVmZTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmMTYyMTFiOGZmOTg3NDZjYjdjYjVkNmQ0NWU0NTEzODJjZmYxNDU2NzA2YzZiZGMzMGFjMzhkNTY2YjA5ZWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZmOGYxZGRlZDIwNzc0ZGRjMDA0ZTc1ZTVkOGYxNTJhYTUzYTE4N2YzZThjNTZmYjgwZTc5MmE0YjQxM2FkOWMifQ" + } + ] + }, + { + "id": "claim_97ac2b5a79b14505b9496b71d92f3c45", + "statement": "wrap_tool_call receives a ToolCallRequest (with tool_call dict, BaseTool instance, state, and ToolRuntime) and handler callback; handler executes the tool call and returns ToolMessage or Command; middleware can call handler multiple times for retries, skip it entirely, or convert exceptions to error ToolMessages.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/tool_error.py#L37-L80", + "version": "repo-lines-v1:sha256:cf892d6b28827271685f34985af1b0a9090677c06cdd90631a2e0e5e34f8cbc7:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlNzE4Y2FiZmZmNTk5NTQwYzI4OGY1N2FmZWNjMzRlNzU4NmFiNmU2NjhkOWEzMWFhYzUzNjRiMjdkMTM3NjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk0YzUwY2M2MWQ0ZjliYTllNDgyYzI5NDExMDliMDkyZmM3MGZkMGRhNTAzNDVkYzhhNzA3MWNjZDliM2EzZTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNlNDM0OTFhYmY5MGNmMTA1OWQzOTNmOWNhODk5MTM3ZGU3ZWZjODA0NGJmYmMyOWZkYjdmYjNkN2JhZTQ2ZmEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L674-L743", + "version": "repo-lines-v1:sha256:1517f09316398d6f79ced6303713c6df6bacdaf0b70f1bb64d1e963aae1f9c15:eyJzZWxlY3RlZExpbmVDb3VudCI6NzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0NTJkY2RlODgxMjZjNGM1Njg1OTMwOGFjNzY3OWU1ODBkYmJiMWNhODNkYjVkYjAxZTJkMTJmMzVhNDdjZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImViZDAyYjcxNTMxYjgxMzNiYTU2NGI0YzRmMzdjM2Q3NTQ4N2U3MWNmOGQ2MDAwYzk4NDFlYThiZTg2YTliNDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA5OTNhNjRmOGMxNjczYTc0NGFjZmZhYWJlOTJkNzUxZjdmYjJkNWZlMWU5YmUxMzc3MWRjNTQ5MWEzNDBhMzUifQ" + } + ] + }, + { + "id": "claim_512cac02a80c4852bcb63a3ea6031818", + "statement": "ModelRequest follows an immutable pattern; direct attribute assignment is deprecated and raises DeprecationWarning; middleware must use request.override(**changes) to create a new request with modifications, preserving the original request.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L169-L269", + "version": "repo-lines-v1:sha256:1f1d799ee979bd940ea7fd5728c1226fbae2705e3e686938bba6fdf621325d43:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJlNzI0MGY5NTNiN2Q0ZTA2NmYyY2UxMTIwYmVlMTAzYjRkNzQ2ZjAwNjVhZTg4ODY3ZmFiYzA2ZDZmYmY4NWQwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1NzZhMTRjYWViNDY3MTJkNzBjNmJmOWEyYzhkNWNiZTY4NzdjOTJmMWM3YmFkNGQ2MjA3ZTNiZmUwYzNkN2FiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIwZjA5YWY1YzdkOGMxMTViMjRhOWQyZDQ2OWRjNTE3ZjkwYWEwNDU2M2E2MmU5MWYyMmRiMTQzNGY1ZTEyZDQ1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2YzFmMDJmZDdlYzFkNDYyYzBjYjgxOGI5NDhmZmU0ZjcxNWIyYzJjNDMyNjMwYzIzMmRmZTM0ZDQyNzQzZmE5In0" + } + ] + }, + { + "id": "claim_00e574d139f249e08fa25693fb5edff0", + "statement": "Lifecycle hooks return optional state updates as dict[str, Any]; updates are merged using LangGraph reducer semantics—the messages field (with add_messages reducer) accumulates rather than replaces; non-reducer fields are overwritten by the most recent update.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1154-L1172", + "version": "repo-lines-v1:sha256:0ee6d1dddc7be26d618eb0a79b7bbe6d7725474309346bf9766e71df3bb227b9:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjMTUyNDMwZjIyZTI4YzNkOWIyMTU3YzA0ZTUwYzFjOGE3MzliMjgyNWI3OTRkMzIyN2QwOTJlYThlYmQ2MDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg5YmE3M2IxNzM2ZjRjMjJlZGI1OTJlM2VhOGExYTM3MmNhNGQ3MDM0ODEyMWFlOGIxNjA2MDFlNDE1Y2FhMDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUxNzU0ODY0NzFjM2JjYzVmOWU2MjQwOTBkMTYzYWFhYzhlNTA3ZWFmNGQ3MGIxMGEwYTg2NDA5NWZiYjhhMDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlOGE2NWIxZGMzMzM0NWExYTY4MWNlYjE3OTJiNzhhNGVlNGM4YTk2ZjBhMjcwYTAyMTY2NmZjYmVlYTE1OGQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L349-L355", + "version": "repo-lines-v1:sha256:44503c793dd9f893d5ba0802082d28f5068e87be55e6e47ed7bf291c25e95709:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNGFjZWQyZjg0ZWIyYWYyZTEwM2QyMGNiMDBjY2ZmYzlmNzI3NmJlMWU3ZTc3ZDdlNzQ2M2U4ZjVjN2RhNjMxNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDU1MDllNzI0MjBmNWM3NGRkY2MyYmE2YmY5NzI2Y2Y4Mjc4MDYzOWIzMTdlNTIzMDY4ODNiN2YyNDkyNjJlMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDcyMjVkNDVlNGY2YmZmZDJlZjYyOTExNjJhNmI3NjM1OTQ1ZmQ0ZTgxZDFmMTk1MGUyYzE2ZDFhOGYxNmY1YSJ9" + } + ] + }, + { + "id": "claim_146d4170ba854d6d8f27e3cc012721a2", + "statement": "ExtendedModelResponse allows middleware to return a Command alongside the model response; commands from multiple middleware are accumulated (inner-to-outer order) and applied sequentially to state after the model node completes; messages in commands are added through reducers, non-reducer fields follow outermost-wins semantics.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L284-L300", + "version": "repo-lines-v1:sha256:9b8b5efb54adc30f9af684ab9d439f7759c4ec88d4f6c69086dcc164df2bef3c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUwZjc4MzAxZThmZjUwYmJiOGNmMjNmZWFlNDhlYWYyYTBjZmUwMDYwYjhkNjMzNjcxYTg4OWViN2VjYjk0NjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIzN2ZjZjdjZjQ0YWUzYzQwMzU3YTA3ODMwZjRiYTBjNDcyYjY4ZGYyY2VmNmQzMDczZTVhNTdjMTMwMTQwZjMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM0M2E5ZmZkMDUyNTI1MTJhZTA2MDNjMDIwZmZhOTIxYmY1ODFiN2M2MDYwOGJjMjcyZWUyYTk2ZWNhN2FiZDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY0NjdkYTdmMTg2NjUxNDY3NGIzZTQ2Yjk4NTNmMmQ0ODBmYzEyYjQ3MjhhZGI2ODQ3MjU5NTJhYjcwNjA1MDEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L290-L312", + "version": "repo-lines-v1:sha256:adb6dbf91250c67b61480c9fa88146be0d316818b7c66c56cc6626e40b1e2bfe:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjMGNjZTk5ZjhkYjAzYWNjNTY1OGE0OWUyZDVjMjEwY2Q3MThlYzIzNGU1ZWYyMTc0MWEyYThhM2I2MDAwOTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyMTE2Mjk0ZDU5M2IzNjY3MjYwZGVmNDViNTQ2ZDM2ZjkyMGQ3Y2Y5ODU3YWRjODE5NTQyZjBkZDJlMWZlZmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU2ZDZiMWJhZDkxMjQzZDNhMTliOTMwYTY1ZTdmOGRlMDExZTAxZWJiYzA2YTczMjc0Y2Y2NWIyNmI1NDIxNGEifQ" + } + ] + }, + { + "id": "claim_a56ed60b4f4247afba9c18fc61586b16", + "statement": "ModelRetryMiddleware wraps wrap_model_call to automatically retry model calls on transient errors with exponential backoff; supports custom exception filtering (tuple of exception types or callable predicate), configurable on_failure behavior (continue with error message, re-raise, or custom callable), and optional jitter to prevent thundering herd.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/model_retry.py#L221-L273", + "version": "repo-lines-v1:sha256:81b64006e47dcee95e25c9e0a3c25246876a050db7cce4edd0d04618a3a7d908:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyNzFhMGExMzM4YjZlODZjZGFiNjVjNjliODU0NzgxNzIxNWJkYjA0M2MxMDY3ZGM0MTU4MmUwMjE1ZWVmZTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkYmJlM2U0MWE2YTcwOWJmMTY4YWYzNzIxMTg0MWIyMmExMjVjNGQ2NjczMDBmNGNmNzgyOWZjYWE4MTQ5YmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIwYjRhOWJmMzg5YTMwZjU5NzVkZTk1ZTJjMDYyMDFiNjJmYTVlNjQzZWU2MmNiMWJiYjYwOGNmZDRlNmUxN2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzc1NjhmMzcxYzY2NGZhNzM5ZmU2ZGYzNzAyZTc2MjUzZTA3NDMyOGQwMjU2YTRhYWQzZGVkOTlkN2Y4YTQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/model_retry.py#L33-L114", + "version": "repo-lines-v1:sha256:01dc3762317bcffc6aa98d7f8450765eafa8ad1ddee80c2aa4f013ad1cbdf81f:eyJzZWxlY3RlZExpbmVDb3VudCI6ODIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg4ZWQzZmQ1ZTg3ZWIxMzUzOTQ0ZGI4YWFhMjY0N2FjYTRiYWM2MTlmZDI1NmMwYmQ3ZWUyMmM0N2FjMjIyNWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZkYjM2NTMzMjU1ODlhYjIyZDNhYmFiNTVjYmEwY2QxY2ZjYWE1NjFmNDg5ODQxZDk3NDMwYWY2Zjg5ODAwYzkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQzMzEwOTVkNWZkOTQ5N2Y0M2RkNjkwMWE5M2Y3NDNiYjY1NTUwNmEwNTBlMTVjMjgxNWJmMWM5ZDRmMTMyOTgifQ" + } + ] + }, + { + "id": "claim_aab2cc6550404035b7e3d20943a3d2bd", + "statement": "ToolErrorMiddleware intercepts wrap_tool_call to convert tool execution exceptions into error ToolMessages sent back to the model; on_error handler (sync or async) receives exception and ToolCallRequest and returns content (str or list of content blocks) to create the error message, or returns None to propagate the exception unchanged; handles only exceptions explicitly returned, propagating control-flow signals (interrupts, parent commands) unconditionally.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/tool_error.py#L133-L209", + "version": "repo-lines-v1:sha256:99f5943e4be660c95eea4414d4a504cce2793228105ba698fb0564f90e1e8d51:eyJzZWxlY3RlZExpbmVDb3VudCI6NzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM0NTJkY2RlODgxMjZjNGM1Njg1OTMwOGFjNzY3OWU1ODBkYmJiMWNhODNkYjVkYjAxZTJkMTJmMzVhNDdjZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MDMyZTk2NGQwOTMyMzUxZTVkMTg1MjY5MjgxYjE3MWEzYTA5YjA4YjI4NmU2NjFlYTQ3MDMyM2M4MmViNGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRhMzUwMTE3NDA5OGVlMmJlY2NjYmFmOGZmODQ0OWRhMTc3OGFjMDI1MzMyYmY2Y2M2M2NiZGY4ZTFlMjgyYjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/tool_error.py#L37-L80", + "version": "repo-lines-v1:sha256:cf892d6b28827271685f34985af1b0a9090677c06cdd90631a2e0e5e34f8cbc7:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlNzE4Y2FiZmZmNTk5NTQwYzI4OGY1N2FmZWNjMzRlNzU4NmFiNmU2NjhkOWEzMWFhYzUzNjRiMjdkMTM3NjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk0YzUwY2M2MWQ0ZjliYTllNDgyYzI5NDExMDliMDkyZmM3MGZkMGRhNTAzNDVkYzhhNzA3MWNjZDliM2EzZTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNlNDM0OTFhYmY5MGNmMTA1OWQzOTNmOWNhODk5MTM3ZGU3ZWZjODA0NGJmYmMyOWZkYjdmYjNkN2JhZTQ2ZmEifQ" + } + ] + }, + { + "id": "claim_8bc539a2478d490e89c935a82740c877", + "statement": "HumanInTheLoopMiddleware uses after_model hook to intercept AIMessage tool_calls and send HITLRequest (with ActionRequest list and ReviewConfig list) to a human via langgraph.interrupt(); decisions (approve, edit, reject, respond) modify or filter tool calls before execution; edit decision revises tool arguments, reject sends ToolMessage with user feedback, respond skips execution and returns synthetic ToolMessage with human's answer.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py#L217-L276", + "version": "repo-lines-v1:sha256:2764bf666b6b99a2710ca974140ec12a6d6aff0a99a588b97024223dff9f3d3d:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYxNWRmMmI2MDc2NjFmM2ZhMjRlMzNjMjA2YTJlMTU2YmUyNmMwZDJmOWZkNTVjOGFlZTk0OTBiYmIxN2Y5NzciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc1ZDRkMjAyOTQ3MDFmZjkwYjkyNzg5OWMwMmIwZjg4MGU4Yzc4NzMxNDg2MGQxNGJiYTQ2YmY3NjFlYTZjNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBkMzk2MjgxZmIwMWQyYmJhZjI2MjY3OWFlNWEzOWRlYzY1YWMwODI2ZWVlZGQxZWYwMDU0OTdhN2ZhMGI2Y2MifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py#L405-L492", + "version": "repo-lines-v1:sha256:429c16826e242b9ccaf9dca3d6505ac7391120daab38cfc33c3ec3d77f0120c6:eyJzZWxlY3RlZExpbmVDb3VudCI6ODgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzZTAxMDE1M2ZkZjExNWY2NWJhMDc2M2QxMjMwYmFiMDVlZGJlMmVlZjAzMTNmN2QwNWMzMDdiYTYwY2MyOGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiNTJiNDgzZjcxM2I4N2JkODBiODExMzZhZDYwMDczYmJhYzgyZmNhZWMyMWU1MDk5ZDE2M2RkN2ZhMGE0YzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ0ZjFkY2I1ZGY0ZmVjZjljOTFkZjUzYWFkNGZmZTMyY2RlNjU0OTlmNmUyMGI2YTZjMTk0ZTgwODU3ZjM4ODYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY5NDIwYTZkZGJmYjhhNWJmZTJhODIxMWU2NjMxODRiZWNhOWI1ZTM3Y2RhYWE5MTZjYjc2YzUyNWY0MWQ1MWQifQ" + } + ] + }, + { + "id": "claim_e187ea10ed4540d990bc8fdfdf201304", + "statement": "Middleware can provide sync-only (wrap_model_call, wrap_tool_call), async-only (awrap_model_call, awrap_tool_call), or both implementations; agent invokes the appropriate version based on execution context (stream/invoke for sync, astream/ainvoke for async); omitted implementation raises NotImplementedError with guidance to implement both versions or use decorators.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L586-L648", + "version": "repo-lines-v1:sha256:02ec2ae5e69994ebc87074777832d3b5541597a60ad1d839116565487e83d158:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4MWMyZjYyYjFiZGE4MWI3MDA5Nzc0NTlmNzJkZTYzNDcwNjk0MmRkZDRlMWFiMjY0OTE1Y2Y0YzEyYTVmOGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzM2IzNjc5NjdlODc3ZWJhMmRhMTZmNDBmYjljMDgwYzAwZjM3MmNmODE2ZmU2N2MxNjczMWNmMzJlN2FhNGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIxOTRkY2VkYTZlNDY5OGY1ZTg0MmJmOWI2NjEwMzJjMWU0MDJlZDhjNWEyNjk1MDgzMGQyODVkMzE3ZTc5N2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRmOTM5YzBiMDdhZjM5NzU1Mzg5YTg2Njg0MDI5OGUyYjEwOGRmMGRhMjEwZGViNTU4MDQ4OGRkODcyNmJhZGYifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L744-L823", + "version": "repo-lines-v1:sha256:6d34bbcf079fbb902a8f342ba3e083f273a28708ff48508f53ef5d1b08a5da1d:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4MWMyZjYyYjFiZGE4MWI3MDA5Nzc0NTlmNzJkZTYzNDcwNjk0MmRkZDRlMWFiMjY0OTE1Y2Y0YzEyYTVmOGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzM2IzNjc5NjdlODc3ZWJhMmRhMTZmNDBmYjljMDgwYzAwZjM3MmNmODE2ZmU2N2MxNjczMWNmMzJlN2FhNGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFkNTMxYmUyMzU3ZDBmZjg2YzAwZTM0OGE2ZGFlZmMyMjYwYTA1NWExZTZmODMyYzRhZDc2ZDgzMjk5ZGM5MzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcwNGU5MGVlZjM0ZTAxNGZiOTM4N2RhMDA3ODhmN2U2NmVmMGM5MzQxODQ2MzA3NmE4NzljMGUxNWQ2NGIxNmUifQ" + } + ] + }, + { + "id": "claim_d897440547174492acc8870c559a6a1d", + "statement": "Agent factory collects middleware with wrap_model_call and awrap_model_call hooks, composes them into single middleware stack via _chain_model_call_handlers and _chain_async_model_call_handlers respectively, establishing order where first middleware becomes outermost; composed handler is traceable and wrapped individually with middleware-specific trace policy.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1154-L1172", + "version": "repo-lines-v1:sha256:0ee6d1dddc7be26d618eb0a79b7bbe6d7725474309346bf9766e71df3bb227b9:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjMTUyNDMwZjIyZTI4YzNkOWIyMTU3YzA0ZTUwYzFjOGE3MzliMjgyNWI3OTRkMzIyN2QwOTJlYThlYmQ2MDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg5YmE3M2IxNzM2ZjRjMjJlZGI1OTJlM2VhOGExYTM3MmNhNGQ3MDM0ODEyMWFlOGIxNjA2MDFlNDE1Y2FhMDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUxNzU0ODY0NzFjM2JjYzVmOWU2MjQwOTBkMTYzYWFhYzhlNTA3ZWFmNGQ3MGIxMGEwYTg2NDA5NWZiYjhhMDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlOGE2NWIxZGMzMzM0NWExYTY4MWNlYjE3OTJiNzhhNGVlNGM4YTk2ZjBhMjcwYTAyMTY2NmZjYmVlYTE1OGQifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L263-L352", + "version": "repo-lines-v1:sha256:272c0933c5414b8cecc2e64643c365fa65e76088a91d385cdab443f37076f035:eyJzZWxlY3RlZExpbmVDb3VudCI6OTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1Y2IwNmM0MzdmOTk3ODhiZDVjOGM4MDY5MjU0MTI5YmVlN2ExNGYzMzc2ZTFjZWY2MTczYWUyMGE3Y2YyZjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViZjVlNjBjNjUyNDc1YjVjNzhmMzdlYmI2OTJjNWIxM2NhZDMwMjI4NmM3MDFlYzBhZTAxODVhMWZiMzVjZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI4YjE2YTdhZjFkZDFlYzI4OTY3MWRhMWU5MmExY2UzN2I4MTA0YWFlYjZlYjU3MzY1NmU4NWVhMzU0YTNhY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU5YTcwM2FiYzE5MWZjZWUxOWIwMzM5NTc3MjEzZWQ2OTMyMGRlOTFmZWMwNDdjZjAzYTY0NTUyYzNiYTY1NjQifQ" + } + ] + }, + { + "id": "claim_9b8109594be04dc3a55f344a954e698f", + "statement": "AgentState is a TypedDict containing required messages field (using add_messages reducer for accumulation), optional jump_to field (ephemeral control signal for before_model/after_model hooks to redirect to 'tools', 'model', or 'end'), and optional structured_response field (holds parsed structured output when response_format is configured); middleware can extend AgentState via state_schema.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L349-L368", + "version": "repo-lines-v1:sha256:6341ae8ca446ca57aee55bd0032315147777efbe4703f1942fb216a34a7534dd:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRhY2VkMmY4NGViMmFmMmUxMDNkMjBjYjAwY2NmZmM5ZjcyNzZiZTFlN2U3N2Q3ZTc0NjNlOGY1YzdkYTYzMTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ1NTA5ZTcyNDIwZjVjNzRkZGNjMmJhNmJmOTcyNmNmODI3ODA2MzliMzE3ZTUyMzA2ODgzYjdmMjQ5MjYyZTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM5YTEyNTZkMDkxOGM3ZWJkYWYzNjhjNTczN2NjMjY4MTVhYmVhNWUwNmVmYTRlMzllNmM3NTc2N2Q4ZDQ4ZDYifQ" + } + ] + }, + { + "id": "claim_513c671976814068b686a241b3d7510e", + "statement": "Middleware can configure trace_policy (optional TracePolicy instance) to control what is captured in hook spans; TracePolicy(process_inputs=omit_payload) omits input payloads while preserving span and timing information; hooks are automatically named in traces as {middleware_name}.{hook_name}.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/tool_error.py#L82-L82", + "version": "repo-lines-v1:sha256:cf1a1e8504c89c90b01d6a80b7ee55b0cbcad23f7375cf1cecb2be8f622475b6:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiY2YxYTFlODUwNGM4OWM5MGIwMWQ2YTgwYjdlZTU1YjBjYmNhZDIzZjczNzVjZjFjZWNiMmJlOGY2MjI0NzViNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiY2YxYTFlODUwNGM4OWM5MGIwMWQ2YTgwYjdlZTU1YjBjYmNhZDIzZjczNzVjZjFjZWNiMmJlOGY2MjI0NzViNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjcyYTg1MDYzMWIyMjkwZWUyZjM3ZjBkYTJkMzRiMDI2MTA1YTQ0ZjMwNTg2YWQwZTUxNzg1YzJmODQ1NWYwZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDMzMTA5NWQ1ZmQ5NDk3ZjQzZGQ2OTAxYTkzZjc0M2JiNjU1NTA2YTA1MGUxNWMyODE1YmYxYzlkNGYxMzI5OCJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L403-L411", + "version": "repo-lines-v1:sha256:c1763344c9294fe2d4b01d56225f97942d1faa5fd1d004fc088d1fc357aec39b:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYjU3OTBlZjUyNjU2ZWVmYmE1ZGJhOGNjNzM4MWRmNzVjNDY0OGU3NWMyNDMwNGE5ZmExZDM4MTM2MGU5ZThjOSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTZhNzE1MjU3MmVlODRmMTA5MDZjNzRiOWYyYTkyNDM0ZGRhYjBlM2M0MjgxM2IwNGU1OTdlZmNlYzk1MWIyNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNWY0NWFhNThlYmM3MmZjNDlkNDFkOTA5ZmM0NjAwNDY4MmMwN2Q4MzRhNWU1YjUxYjY5NmU2Y2M2ODc1N2I3NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGFlN2EwY2FiYzQ1MGNhNDk5ZTE4OWI3NjIzZjBjNGRkZGQ5OGQ0ODgzNmJmNzQ2N2FlY2ZkZmMwNWUzNWE4ZiJ9" + } + ] + }, + { + "id": "claim_158f56cfa5734589932dbcef02ad69d6", + "statement": "Middleware can register additional tools via the tools attribute (Sequence[BaseTool]); registered tools are merged with the agent's tools during graph compilation; example: ShellToolMiddleware registers a shell_tool.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py#L400-L401", + "version": "repo-lines-v1:sha256:7ef1873b30d8420a5f483c882f53c472a9a66cdd2300c0a6b2eb017f7231fb8b:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjkzMzljMzIxNmJjMDNjMjFmNDQzYzdhYjdmN2M2Y2JmMDFmMTUwZmIyMjVjMzcwNGRjOWQ2NTdhYWM5Y2QyZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDkxMzU2OTQ0NGM1ZjQwZjJiMTZkZmU3Y2EyYzc2YWJhYjgwODk2M2RiMTY1ODM5OGU2ODY5YmNiODg2ZWUzNyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmNlZmM3YzlhOGMyNDMyNmM2MGIwMGNjN2E3YzIyZjMxMTQzNWE0OWFmMTMzNzg2ZDczZWExOTcwNTIwNzMzNyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNGViMjc1MTdkNmUzZjFiMDA4MTUyN2MxZDdhNzRlMTJjMjU2ZGZhZjhkOTRkNDllM2UwYTE4MGIxM2NmZDAwMiJ9" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/model-initialization.json b/openwiki/.claims/model-initialization.json new file mode 100644 index 0000000000..5e619051f2 --- /dev/null +++ b/openwiki/.claims/model-initialization.json @@ -0,0 +1,182 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:0ff4006082d29f33169349313c83cc890f4fafd73f0a3df4a51cf88689471825", + "claims": [ + { + "id": "claim_189e539212e149fdb6d26bfac3426a8c", + "statement": "init_chat_model is a factory function that creates chat model instances from provider strings with optional prefixes (e.g., 'openai:gpt-4'), supporting both fixed and runtime-configurable model initialization modes. It centralizes instantiation across multiple provider integrations, handles provider inference, and routes parameters to provider-specific constructors.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L230-L240", + "version": "repo-lines-v1:sha256:2cfac04a6db674cab340eadb38f47ef218a5f55df7f298a7b8342703a228272b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMwYmQwZTkxYzk5ZDQ4YzAyZGZjYjliMGZlOGYzMGQyYTE5NTY0ZDQ4Y2FkOWJhYTM4M2MxMjY1MTk1YTIwOGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkzMmMyZTNhNjY4MGUwYTc0ZDMxMzNiYTUyZTY2ZTBhZDY0MzM1ZGNkZWNkODdmOGI1NWU4ZWI4N2M2OTdlYmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRiZDBkZGM4Njg0NTlkNmJlN2EwMWRmNmY0ODUwOTBkNzhlZjNkOGM3MzZiOGY1M2JmMTZhZmUxMjI2ZTc4MWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY4NmU2NmVkNTc1NzhmZTc4ZmJjM2M0NjEwNzJkODVmNDJiZjBjOWRmNGI1YmFhN2U3MzcwNDYzNzllOWE1OGMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L515-L529", + "version": "repo-lines-v1:sha256:5c82190ce075e70f696f08fdf2ebfb113c6283ef02d77f557df3b93a6b354202:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxMzcwMzkxYWE5MmJmODBiY2VjZTVhMDdlYWY3YTc3MGJiNDUyZmU0ZWI1MzhmNWVjY2MzMWM1YmM3Yjc0OWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUyZTMyMDZkZjg1Njg0Y2E5OTE4ZTU0Y2RmZDA5MTAwNGM3NjZjMWYxZjRmYWQ4ZDA3YjM0MzkxOWUyZmIxZDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImFlYWI5MWUyNzg3Zjk0YjdmNDU0ODAxYjliYWE5MTI1ZmQxMzRkMzc3NGE0ZmU1Mjc5N2M1ZDMzNWRiMGM5NDgifQ" + } + ] + }, + { + "id": "claim_df4133df06044486afe159623791dcc7", + "statement": "init_chat_model accepts model (str or None), model_provider (str or None), configurable_fields (None, 'any', or list/tuple of field names), config_prefix (str or None), and **kwargs for provider-specific parameters. It returns either a BaseChatModel (fixed) or _ConfigurableModel (runtime-configurable).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L230-L237", + "version": "repo-lines-v1:sha256:40f16a565f3062c4122d4e9125dd6ee970cd6047f1b144d1446dc683febfe94f:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYzBiZDBlOTFjOTlkNDhjMDJkZmNiOWIwZmU4ZjMwZDJhMTk1NjRkNDhjYWQ5YmFhMzgzYzEyNjUxOTVhMjA4ZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDIyMTA2ZWFkOGE5YThmZWYyY2U1MGE1NzEyNTcxY2YxOGFiZTI2MWE4Y2E1ZDJiMjI1NmEwMDc5MDQ4ZDZjNCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZGJkMGRkYzg2ODQ1OWQ2YmU3YTAxZGY2ZjQ4NTA5MGQ3OGVmM2Q4YzczNmI4ZjUzYmYxNmFmZTEyMjZlNzgxYiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzllYWZmNThmNWQ2N2E1YzY3NWI3ZTlmNjM1MWY3N2I5N2RjNDMzZmFiMjFhZjY5Yjg3NzMzMTg2ODQzNzk4MiJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L381-L386", + "version": "repo-lines-v1:sha256:635fe4218d2ccfda31f2f659d7b3119e12d002612fdb21e3d1588084a6c4989f:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMmY4NDYzMTBhMDg3NGY0ZDI2MjFkZmU4MjNiYTdiNDY0MjJmMWMwYjc3ZmRlZjYyOGU0YzdiZGM1ZTZiYzljYSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjA5OWJjMjM0NzExMzZiMTY5ZWUyMmJiYzc0ODlhNTI3M2JiM2Q3NDVkMTMxNjU3NWZhY2ZiZmMyMjAyNjIzYyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOTY2OTMyMDI4YzQzNDVkMzlhNzM3ZmI4Y2E1YzRjOTUxYzBjZWJmYmI1NmE0NmMzZDQxOWQyODFlMDUwNzczNyJ9" + } + ] + }, + { + "id": "claim_b02fe80251ae4429b2eadcadeede3569", + "statement": "When a model string contains a colon and the prefix is a registered provider key, init_chat_model extracts the provider and model parts; otherwise it attempts best-effort inference from the model name prefix using case-insensitive matching against known patterns (gpt-, claude, command, amazon., etc.).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L543-L616", + "version": "repo-lines-v1:sha256:a9fa9d961e85cbbccd7d57b4421babf5fbe5d93a7aa62b3bc0b38d6b368121dc:eyJzZWxlY3RlZExpbmVDb3VudCI6NzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3NjVhMDM2OTVmYTJkMWU0YmU3OWZhYzhmMDNhOGMzOTU2OTI3OTcwMjY0MTMxNDU5NDUzMDc3ZmU0ZjM1NTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTYwMGE3M2QyZTAyZjM1MmI5MGZlZjFkOGQ2MzgxMWJhNGE4ZWUwYzFiZWFhN2FlNDY1OGYzNmFhMmUxOTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlNTMyODkxMmQzMWM2MTZhZWI3NzBhM2M1YmI3ZmIxZTE0OTI3MTRiNjQyNThjOTdhZjJlYmM0MDIxNTdkMmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllNDUzZTU0YTQ0ZDU0ZDAyYWNiZDViOWE0YjBlNTIxNTQ4ZGM4MTY0ODFjYTBhZDM2YzBiNmFhMzdhZmNiMDcifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L619-L648", + "version": "repo-lines-v1:sha256:7ed3f3ed659bea78a63bacae1bc7cb6bc9fe0c7e7ec32215b6e5561ac495d752:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA0NzUyNzA5Y2ZhODMyODY3NTc4ZjRiNjU2NWJlZGQ5ZmJiYzJlNTkzNTRmNDU5ZTZmM2RkZDgxZjNjNjAyNjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MDE0OWUwNjlhZDY3ZWNjZjQ3MzY0NmZmMWM3NTAwNTBkMzI5ZDExMDAxOGRjNTc2Njg3OGVhZmExZDYwN2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEwZGZjMjQ1MDYxYTM4NTQ0MmQ3YmM3ZjY5OTk1ODAyZjE2N2QwN2Y3OTI1ZGZlNTI4YTM3YmUwZWU0ZjQ4MWYifQ" + } + ] + }, + { + "id": "claim_b11a1705a5cd44ea88dd0a9a4565a98d", + "statement": "_BUILTIN_PROVIDERS is a dictionary mapping provider keys to tuples of (module_path, class_name, creator_func) for 37 built-in providers including openai, anthropic, bedrock, google_vertexai, azure_openai, cohere, groq, mistralai, ollama, and others. The registry includes fallback logic for ollama (tries langchain_community) and special creators for huggingface (from_model_id), ibm (model_id), and langsmith (gateway config).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L56-L120", + "version": "repo-lines-v1:sha256:a714caf3bde31adb33a627fe7c985aa333571e5693027c2ef4131d0168cfc131:eyJzZWxlY3RlZExpbmVDb3VudCI6NjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0OWFmODNkYjQ4ODc5YTQyYjVjNTliMmEyYmY3NThiYWY4YjFkMmQwNTJmOGRmN2VlNWNkOTIyMjJkYTM4NzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJkNzA5NGEwOTBkZDkxYjgwMmJjZWIxYWNkYzMwYWMyNTc5MjU4YTFjNjkxNTk4YWVmNjYyMDAyMWQ1NWMzMDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgxOTY2MDgyOTFhZjY5NzQ1YzViMmVhZDliM2ZhYjRkMjVkZjQ2MjZkNDNhMzQyYTA3ZjkyMGRiYzhiMmJiYWMifQ" + } + ] + }, + { + "id": "claim_8cf40321717f40ad867f7c85ceb2142d", + "statement": "Most built-in providers use the standard _call creator function which directly instantiates the provider class. Special creators include: huggingface (uses from_model_id class method with model_id parameter), ibm (maps model to model_id constructor param), and langsmith (wraps instantiation with _apply_gateway_config to inject LangSmith gateway credentials and base URL from environment).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L27-L38", + "version": "repo-lines-v1:sha256:6e5b778f54e6269116c7c108fc0539dcb3d7f95e0e3529de1c59b46699e93250:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVhMTQ0MTg3N2RmNDEzMmJlOTc1MjBlMDUwMWI2YjAzNTFkMjVkMDNiNmZhZjNmYWQ2NDk0MTZlMDBkMjM4ZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjMGEwMjQ5NGY3NTViMTQ4YWRlYmMyNWI5MzU3ZDA5MjAzNTM3YWY3OTcxZDE0OTBiZWM3ODFmMjFiNDY1YmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2NTdkMjA0ZjBiNWVmMmU3ZjAzYWM3NGE3NGJmZDhmY2U1ZTVkNzU4OTkwNTYxODU3MTY2NDRiMmY3YWUxZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkZTlhOTVmZjI2NTMxOWVjM2U1ZjdlYzQ1ODFlNjBiYTc3NTA3Y2I3OWY5YWM4M2IzNmNmZjU5M2FjMGRjNWEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L51-L97", + "version": "repo-lines-v1:sha256:fbbd5e179b2a760321d8bfaedb870aace34be1b417b6f3a4c3703a9a455bfa25:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjZGEyZjQxNDVlNWI3YTZjMjNiZmNlYTE4ODc2Y2QzYzA1OWUyNGYxNzI2NzVjMzMwYTMzYzEyY2JjODVjNzgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA1N2QzZGM0Yzk1NTU4ZTVjMTZlODkxNGQzMTJiYWVkYTMxNWZjZjJlMDE0MzM0MDBkMzU0N2I4MDE2NjljM2YiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhYzk3OTRjOTIxODcwZGVjNjU0YWU5YjU2ZDBiMDk3YWU0NDlhODU4NzE5Y2Q3MjhkZDI5N2ZhNjMyN2VhOTYifQ" + } + ] + }, + { + "id": "claim_52206704126944f1bc8bac7cea75f27d", + "statement": "When model is a non-None string and configurable_fields is None (the default), init_chat_model immediately invokes _init_chat_model_helper to parse the model name, retrieve the provider's creator function, and instantiate the chat model with merged kwargs. The instantiated BaseChatModel is returned and ready for invoke/stream immediately.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L515-L520", + "version": "repo-lines-v1:sha256:beb5925717f9dd1ecbc7d419ac81f59aeffd3a2ef9ba50313eb8edd82b89d904:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjEzNzAzOTFhYTkyYmY4MGJjZWNlNWEwN2VhZjdhNzcwYmI0NTJmZTRlYjUzOGY1ZWNjYzMxYzViYzdiNzQ5ZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTkwOWE1M2E2MThkMDI4ZTFkMzAwMGMwYTdhYmNlZWNiZTVmYjM3ZjRlNTExZWZlNDY2NDNmYWQ2ZTY2ODcyZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTJlMzIwNmRmODU2ODRjYTk5MThlNTRjZGZkMDkxMDA0Yzc2NmMxZjFmNGZhZDhkMDdiMzQzOTE5ZTJmYjFkMiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNjkyNTJkZDk3MTFkY2JmNGQ3MDI5MTQ2YmQ4YjYxYmFjYjgzYjFhNTc5MTMyNzlkYzEzZGIxNGE2YTJlM2UyZCJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L532-L540", + "version": "repo-lines-v1:sha256:85d94840b2951465740c4d8b5810394b3e5238bf6e32959da62a0e04828d3036:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMGFiYTlhYWY2OWJiYWJiMzc1MjI3OWVjM2Y0ZmJhMTM0ZjI0Yzg5MjA2YmQ4NWJmMzM3M2U5MzA1M2E0NGFjZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNmIwZTE5Y2U4NTZkNWY5Mjc0NTZkOGMxOTc1NmNlN2FiNjJiZDVkNDViYTkxMjgzZmI2MzFmMzAxYzFmMWUyYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOGNjNGE3ZDFjYzc5NmFiYTNlNDY3MjMzMjdmNTE3ZTlmODExOTgyZDI1MGYwNzAyOTg2ZTRiNTc2NGFkOTU4OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDJiMTNjODdmZDQyZjM3MjE5M2NlOWFhN2VhNTY1OTBlZWRiMzllZGM4ODE4OTIzYTQ1ZjJjOGY5YzFjNDQxMiJ9" + } + ] + }, + { + "id": "claim_1ad24cbf24204f71804126cdce8e0935", + "statement": "_ConfigurableModel is a Runnable[LanguageModelInput, Any] that wraps init_chat_model when configurable_fields is not None or model is None. It queues model instantiation and declarative operations (bind_tools, with_structured_output) until invoke/stream is called with config, enabling runtime selection of model, provider, and parameters.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L521-L529", + "version": "repo-lines-v1:sha256:34bcbfafa7789ef02b2356f9db768c3a24cc1a0e42628ec8caaf5f5396b4117d:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODM5ZWYyNWIzODVhM2Y0Y2I0NDc4OGFjOGUzYTkwNTM2N2Y4YzA1NTg1OTVjZjkzNGMzNDFlYWViMzU1NGRhZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzBjMWYxODhmMTQyZWQ3YTgyNDY4MGIyMDcxNzJjNTUxOWJjNjE0YTQ4ZmRhN2NmMTY3ZWRiOGRkZjc5OTU2YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNGM0NjQ3OWJiZjY0ZDFlZDE2MWY3YzliNTViOTg2YmFhZDEyYmRmZjgxNjBiOTljNTA2MTk2ZDE5ZmVlYmZlOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYWVhYjkxZTI3ODdmOTRiN2Y0NTQ4MDFiOWJhYTkxMjVmZDEzNGQzNzc0YTRmZTUyNzk3YzVkMzM1ZGIwYzk0OCJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L657-L680", + "version": "repo-lines-v1:sha256:bff10cdba27371d04116c8a459852b14f740b4919b6cbf369bb8f26ee2f76609:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM5MzJlZDgzMDcwMjhjNjk2ZDc1ZDQ4ZjFhZTQ5YjJiOTE4OWY2NDA3NTFlYWU4ZGYxZTMyNzc5ZTI4ZDQ2ODgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUyNTE0YmJlNzM5OGY4YTY1MWQ1Zjk1MGNiOGRkOWVhOWJmNjNjYTg3NzRlZjc4YmE4ZmVjZmZiNTk5MTRiNzUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlYWQxMTFiNDUwYzAwMWZlZDdhY2ZmMTBmNGM5ODIyYjJlNjc0ZDQ1OTIzZmMwZWY1ZmE4ODA4ZGNiZjc1NzkifQ" + } + ] + }, + { + "id": "claim_9864f836752745aea729ecab9f689953", + "statement": "_ConfigurableModel maintains _default_config (default params), _configurable_fields (which fields can override at runtime), _config_prefix (namespace for config keys), and _queued_declarative_operations (method calls to apply after model instantiation). On invoke/stream, _model(config) merges defaults with runtime config, instantiates the actual model via _init_chat_model_helper, applies queued operations, and delegates to the instantiated model.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L658-L680", + "version": "repo-lines-v1:sha256:c6b92ea50d4c8f8413cd2b81f455be9be5ade4d21e15e834dd69bc36e0168f8c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlmZDdhMDdhODNkZDE5OTZmYjUxNjg0YjQyYWVjYWY0ZTkwZmI3OGJhNzIzMGU1NDJlZDdiZDAxZDQyMzAxYmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUxMWE2MjBjMDM5NDBkNzcxNDA3ZDY3NzAxNGNiNGMzNzYyMDBmZDZmZDYzMWU4OGFiYTY5ZTY4MTI4YmQ0MzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlYWQxMTFiNDUwYzAwMWZlZDdhY2ZmMTBmNGM5ODIyYjJlNjc0ZDQ1OTIzZmMwZWY1ZmE4ODA4ZGNiZjc1NzkifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L711-L727", + "version": "repo-lines-v1:sha256:208f58933e125c9b5eba0e1ba0a625f611d83d36672a257c931c33e375e84702:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUyZDkwMDc1MWViZjJhNzY3ZjY0YWRiNDI2NzYwZDI4Y2I1NzcyYTg0NDMyYTc2MjRkMmJlMGU1NjI1YzA3MjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI4OWNiNzEyNjQ5NjEyYmI2OTJlZGNlYzNhNjQ5ZTg4ZDlkNWYzZDkwNmMzZjNmMDkyM2RkMDMyYTQ3YzNkMDciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJiM2VlNmUzN2E2ODljZmE3NjZlOGI4NWUwMDkwMDg5OGJkNDI2MDJiYWIyMTFmYWFhMTBkODVjOWViNzcyZWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVkNWFlM2Y3MDYwOGZmOGQ0NjNiZDZkYjVlMDczYmM4YjlhYmI1OGU4YWRkNGM3N2I5ZjBhNzUyMWI1ZGUwZDYifQ" + } + ] + }, + { + "id": "claim_22c8fb84fcbe4610a881583eb77e0000", + "statement": "When bind_tools or with_structured_output are called on a _ConfigurableModel, __getattr__ intercepts these calls and queues the operation with its arguments instead of applying immediately. A new _ConfigurableModel is returned with the operation appended to _queued_declarative_operations, enabling chaining without mutating the original instance.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L681-L702", + "version": "repo-lines-v1:sha256:176787c5900a0ecfbb77c7bcf291defe88731d7120a1477ebcf154902bdfcd18:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM4OTE2NGFlZGRlYTAwOGY5ZjcyNjhjMmJkZDRjZmQ3ZWY2YTEwYzIwMzFjMmRlMzg0NjdhZTI4MDU4ZTRjMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2MzI5NGMzM2Q1OGY5ZGMyMzk0NWY3M2M2YWQxYTdjMjk2MGFlNzAyMjAwMDk5ZDQzNTZiY2VlOTZhNWViMTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI5NjAzNmViYTY2MmI0MGUwNTBjNDI0YTJlZjIwNDRiNmQ1NTY0YzVlMmM0YzE3OTk1YmVlZTExYzFhZTA3MDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNlMjliYWRkNmExMmMyZGMzYzdiNTExYWZjODc4MmJjNjFmYTEyYzY5MGQyMWY2MDNiNjNkODVjYzliMjIwNjgifQ" + } + ] + }, + { + "id": "claim_a78a74767aff40048a7c03b8beb81db0", + "statement": "_model_params extracts model parameters from config['configurable'] with the config_prefix stripped, then filters to only whitelisted fields if _configurable_fields is a list (rather than 'any'). These are merged over _default_config to determine final instantiation parameters.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L718-L727", + "version": "repo-lines-v1:sha256:c4374ce21b4f6fd5954ed1db71f666184e7bcb6ae57d42a2deadc3824c22e4fd:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3ZmRjY2IyZTIyNGQyZmZhZjdiNzNmNzY4YjRkZDUxNTZkMDMyN2Q1OTg3YjdmMzNkNmU1ZmMxY2ZjOTFhM2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI4OWNiNzEyNjQ5NjEyYmI2OTJlZGNlYzNhNjQ5ZTg4ZDlkNWYzZDkwNmMzZjNmMDkyM2RkMDMyYTQ3YzNkMDciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE2NzVhOTgxN2ZlYWEzNWZhNTRhZjQwNGYwMDQwZWI4MDU3ZjQ1MDVmNTlkNDc4ZjIzYTZiOTA1YmNlMmFkYWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVkNWFlM2Y3MDYwOGZmOGQ0NjNiZDZkYjVlMDczYmM4YjlhYmI1OGU4YWRkNGM3N2I5ZjBhNzUyMWI1ZGUwZDYifQ" + } + ] + }, + { + "id": "claim_2b7fd030bdd2424da9713d6f5d347b1d", + "statement": "The langsmith provider bridges to LangSmith's unified LLM gateway. It uses _init_langsmith as the creator function, which calls _apply_gateway_config to read LANGSMITH_GATEWAY (default: https://gateway.smith.langchain.com) and LANGSMITH_GATEWAY_API_KEY/LANGSMITH_API_KEY environment variables, injecting them as openai_api_base and openai_api_key, then sets use_responses_api=True before returning a ChatOpenAI instance.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L27-L38", + "version": "repo-lines-v1:sha256:6e5b778f54e6269116c7c108fc0539dcb3d7f95e0e3529de1c59b46699e93250:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVhMTQ0MTg3N2RmNDEzMmJlOTc1MjBlMDUwMWI2YjAzNTFkMjVkMDNiNmZhZjNmYWQ2NDk0MTZlMDBkMjM4ZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjMGEwMjQ5NGY3NTViMTQ4YWRlYmMyNWI5MzU3ZDA5MjAzNTM3YWY3OTcxZDE0OTBiZWM3ODFmMjFiNDY1YmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2NTdkMjA0ZjBiNWVmMmU3ZjAzYWM3NGE3NGJmZDhmY2U1ZTVkNzU4OTkwNTYxODU3MTY2NDRiMmY3YWUxZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkZTlhOTVmZjI2NTMxOWVjM2U1ZjdlYzQ1ODFlNjBiYTc3NTA3Y2I3OWY5YWM4M2IzNmNmZjU5M2FjMGRjNWEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L85-L85", + "version": "repo-lines-v1:sha256:7388d2845aa2a638c655dd1f452f3548453910726f6c46e285bea68cc344f7f1:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzM4OGQyODQ1YWEyYTYzOGM2NTVkZDFmNDUyZjM1NDg0NTM5MTA3MjZmNmM0NmUyODViZWE2OGNjMzQ0ZjdmMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNzM4OGQyODQ1YWEyYTYzOGM2NTVkZDFmNDUyZjM1NDg0NTM5MTA3MjZmNmM0NmUyODViZWE2OGNjMzQ0ZjdmMSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDE1MDA4YjkyOTk0M2VmNjVjNjZmZDcyYzAwYzZkZjc2MDFjNzI4NmE2MmEyNzQ2ZGE3ZDI2MmMyYTVjMTczMiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMzAwMGFlODZmYzhiZDc3MzBjOTdiNDhkZjljY2JlODc4ODgyZDFhNDY0ZjNiMjE1YTQyMDVmN2E0OTc4NjhhMSJ9" + } + ] + }, + { + "id": "claim_f40e3373b028465c9e4039fef1776032", + "statement": "_import_module catches ImportError when importing a provider's integration package and raises an ImportError with a helpful message suggesting the pip package to install (e.g., 'pip install langchain-openai'). For ollama, it falls back to langchain_community.chat_models if langchain_ollama is not available.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L123-L192", + "version": "repo-lines-v1:sha256:9a2a34a5d66d7b370be31f6393b3660e7b8a32212d987fca26bff006bf1d6e08:eyJzZWxlY3RlZExpbmVDb3VudCI6NzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxMzM5NzBiMzhhNTU5MDE4NjYxYTA1MTE5YjA4MzgzMTk5YWExNWIzYzk4YWI5N2Q1MWQ0ZDM1ODllODQwZjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM4YWU2MGEwOWE3ODRiNDdlYTA4NTMxNmJmZjkyYzY4MDExZGQ2NzBkODlmMjhkYWY1ZGE5YmI2YWU0MWJjNDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEyZjRjNmNhNTllMjY0NGQxZTBlNDg5NDA4ZGZlNTcwZWY5NzlkYmM0NDJiZmVjZDEwZjNkZTExMTQ5YjVlMmYifQ" + } + ] + }, + { + "id": "claim_349e633a7ed74c69adaf52964ad6aa7d", + "statement": "_attempt_infer_model_provider uses case-insensitive prefix matching to infer provider from model name: gpt-/o1/o3/chatgpt/text-davinci → openai, claude → anthropic, command → cohere, accounts/fireworks → fireworks, amazon./anthropic./meta. → bedrock, gemini → google_vertexai (with deprecation warning), mistral/mixtral → mistralai, deepseek → deepseek, grok → xai, sonar → perplexity, solar → upstage. Returns None if no match, triggering a ValueError.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L543-L616", + "version": "repo-lines-v1:sha256:a9fa9d961e85cbbccd7d57b4421babf5fbe5d93a7aa62b3bc0b38d6b368121dc:eyJzZWxlY3RlZExpbmVDb3VudCI6NzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3NjVhMDM2OTVmYTJkMWU0YmU3OWZhYzhmMDNhOGMzOTU2OTI3OTcwMjY0MTMxNDU5NDUzMDc3ZmU0ZjM1NTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTYwMGE3M2QyZTAyZjM1MmI5MGZlZjFkOGQ2MzgxMWJhNGE4ZWUwYzFiZWFhN2FlNDY1OGYzNmFhMmUxOTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlNTMyODkxMmQzMWM2MTZhZWI3NzBhM2M1YmI3ZmIxZTE0OTI3MTRiNjQyNThjOTdhZjJlYmM0MDIxNTdkMmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllNDUzZTU0YTQ0ZDU0ZDAyYWNiZDViOWE0YjBlNTIxNTQ4ZGM4MTY0ODFjYTBhZDM2YzBiNmFhMzdhZmNiMDcifQ" + } + ] + }, + { + "id": "claim_dc1859b860ab4c1686802fd90b694c35", + "statement": "If model is specified, configurable_fields defaults to None (no runtime configurability). If model is None, configurable_fields defaults to ('model', 'model_provider'), allowing runtime selection of both. If config_prefix is non-empty and configurable_fields is None or not provided, a warning is issued.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L504-L513", + "version": "repo-lines-v1:sha256:a3a9526e612e846f52046efbcdc17fed68b4152d1ad0d02c9efc0b7336ff7a31:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiNzhiOWQzNzI4N2ZmM2NmYmNmMzVjZDlmMjA1NGYxNWQ3ZmI2NDEwZTRkMGQ5ZjkxYjQ1MWQyYjRmNDlhNjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg0ODVlZTcxYmI4YmY1ZjVmZWFiOGM1OTczZWY5YmZjY2NkYzUwM2Y1MGY1YjZlOWYwYzY4N2FiMzc0ZjE0ZDciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMzMDRhZjZiYzUyNmQwNGFhMzJkNmYwZjU3ODhhYmNlMmVjZjFhZjg0N2Q5ZTA0YjZkODZiZWEzMTEwMzRiN2YifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/openai-provider.json b/openwiki/.claims/openai-provider.json new file mode 100644 index 0000000000..ffa2ab427c --- /dev/null +++ b/openwiki/.claims/openai-provider.json @@ -0,0 +1,134 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:13741eab09e2d769cdfd885ea9b92e8a5f790a63498d6b10bf6cfeeaecf076b1", + "claims": [ + { + "id": "claim_347664eb68464142b558791c7a95093f", + "statement": "ChatOpenAI is the primary class in langchain-openai that wraps OpenAI's Chat Completions and Responses APIs, with support for structured output, tool calling, vision, streaming, and model profiles.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/__init__.py", + "version": "repo-file-v1:sha256:d08e06dea1f954a477fa8951e2a8387a10396d07b92a3a60fdced307c136a84f" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2799-L2900", + "version": "repo-lines-v1:sha256:2d8081ad2ca1015a79c313cb0513c30f843560a31c19eb962e55f5edaac7b72a:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkOTBkYTg3MTNlNzU1ZjU1MmQ1YmIzNWY3YjJmOGUwMGU3YjA0MzY1Yzc2YWI4MmE3NDRmMGU1OTVlMjA2MDYyIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMGNhZTNkYzI3OGY2NDlhYWFlYjE5ZDdhMTBjODYzNjQyNTIzZjhlZjAwYzkxODUzNjEzNGRiMGM0Mzk4NDU0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJiM2RmMmFjZjA2ZThjM2YwM2M3MTM1YzZlOGFjNWE5Y2E1NDMyYWJlMDZhNzRhY2EwZmRkOWE1M2EwMDhmOTg3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIwMjVmNzA0OWQ5NmQyY2Y4MWE5NjA5Y2NkMDgxMDg0ZmE5OGRhYWIyM2QyMmIxYzMwMjI5MWY3ODhhNjRmM2E0In0" + } + ] + }, + { + "id": "claim_75e7343518fc437f8b13c59e87d5847d", + "statement": "OpenAI API key can be provided as a string, sync callable, or async callable; if not provided, it is inferred from OPENAI_API_KEY environment variable.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L740-L790", + "version": "repo-lines-v1:sha256:003a6dab73ea8fde5dc6459b1b513510f5f2edaf2d067675483617e4bbadc10c:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg0NWZkNDk1ZWU5OWQ1OWFkN2VlNWNlNjc3NzY4NTA2OWZlYzJiMzFlN2VlOWI0ZjlhMjA2M2RlMWRkNjFhNjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ4YzBjYzA4MjViNjY2MWI1ZmM0YWE1MjQ5NmJhOGM5ODIxNTY3MzFiNTM2NTBlYjhiYTYxYmRmNjI2ZmNlNGMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMyNjAzZjZjNDViZmIxODk0YWE5ZWMzY2U4MDJhMTNlMDA5YzcyNDgyMzZmOWIxMzdjMTNiODFjZmQ1NGNmYmUifQ" + } + ] + }, + { + "id": "claim_eb12b471a35f4b3aa689a29f4198b97d", + "statement": "Base URL for API requests resolves in order: explicit base_url kwarg, OPENAI_API_BASE environment variable (read by LangChain), OPENAI_BASE_URL environment variable (read by OpenAI SDK). When base_url is set, stream_usage is disabled by default because many non-OpenAI endpoints don't support streaming token usage.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L792-L804", + "version": "repo-lines-v1:sha256:9f9f2b6365c20f8e4ebc2ea1b16828d2dd71d68a1dd579ae1f9db0643b1c1207:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJhZTA2YjYxYzkwZjNjOTQ4NDVhZDJjZDQ3MGQ2ZDZlNzkwYjIyYjY2MjllOWRkNDA0NGE4MDJhNDQzZmMzMWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhMjIxODk1NWJjM2M2MzdmNGU5ZTQwNWQyY2JmNWU4Yjg4NTBkYTAyYWQxODEzM2I1NDMxNTllMDljZjQ3M2IiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI3MmE4NTA2MzFiMjI5MGVlMmYzN2YwZGEyZDM0YjAyNjEwNWE0NGYzMDU4NmFkMGU1MTc4NWMyZjg0NTVmMGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1YTY2YzhhYmZjZmNmYTM5ZTc4YjUyZWQ2ZDEyNWYyOWIzZjExNGYxZGZjODBiZGI3MzFhZWY0ZmIyNGQ5MmQifQ" + } + ] + }, + { + "id": "claim_d7e278bba6bd44e9b618ca0d72f7cc6f", + "statement": "ChatOpenAI.with_structured_output() supports three methods: 'function_calling' (tool-calling API, default), 'json_schema' (OpenAI Structured Output API for strict conformance), and 'json_mode' (JSON mode without strict schema validation).", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2498-L2760", + "version": "repo-lines-v1:sha256:14c2acc8c738fb447b6995851743559e736d434f21245c843220309621afa05d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiOTk3OTkzN2Q3ZGUxOTkyNzZjM2I3YzVkMTJkZWM2ODJhZTBmOTVjNTliZjA2OTlhNGI2OTRhZDg1ZWQxMTVhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI5OTUyMjA5NzQ4ZWJiMjJhOWM3Nzc5ZWEwMTBkODNiNzUzYmVlY2Q3ZDFmY2M2ODRiYTBmNDQyZjU4MDE4YzMwIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0ZTk3OWE3YzIxZjY0OWE3YWRhZTkyM2I0ZmJmYjQyZDY2N2FkMGU2MGMzOTgyMTU5MWM4MjY2YzFkYzVlNjUxIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlYWYzZTViMjlhNzMzZGY1OWNlNGFjYjM0MDdlZDYwZTUzMjllODgyNGU5NDdlZmRmZTI3MWVhMzI2OTlkZDdkIn0" + } + ] + }, + { + "id": "claim_72e524b08caa4a60958af11e3c7b7090", + "statement": "bind_tools() binds one or more tools to the model and accepts tool_choice parameter to specify 'auto', 'none', 'any'/'required', a tool name string, or a dict specifying which tool(s) to call. It also supports parallel_tool_calls, strict, and response_format parameters.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2390-L2496", + "version": "repo-lines-v1:sha256:33e6d9ef74cf0bf4d7297bdd8e2c381cfac7bb645a908ab7e3175f00717700f6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmMTg1NGIwNDY5MTdlM2FiMmFkMDI5ZTBlZWFlN2Y5MWQwYmUzZDY5MDE0YmI2MjdhYWRkZWYyZTY5ZDM4ODJkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIxMjA4ZTliMjUzOTRiM2I0YWY1MDgyODIxYWE2MjM5MTgyZjRhMTdlYzc0MTFhOGY0MzZlNTEyYTY2ZmU0YWFjIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI1MDg4MDFjYjgwMGUyZjIwMzc0ZDM5YTEzNzZjODU0ZGJlYTIzMGNlMGQ1YWM1ZGIyMDBlMWFjMjdkYjZjYTc3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1YTE0MTYxZjkzYTAyZjM1OWJkODA1ZmViNGI4OTZjNGYxMWVhYjRjNWY3ODQwNmZkY2Y5OTdjZWRjNjdjMmRiIn0" + } + ] + }, + { + "id": "claim_5c43c02cb87c4428a7bc56f79314828d", + "statement": "tool_choice supports 'auto' (default), 'none' (prevent tool use), 'any'/'required'/True (force at least one call), a tool name string (force specific tool), and dict format for OpenAI's native tool choice specification.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2407-L2482", + "version": "repo-lines-v1:sha256:fab7afcc3debe57067cfe831790e2c1f79ec95bbd1c9cbbd0616e6e8f94f59d0:eyJzZWxlY3RlZExpbmVDb3VudCI6NzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjliNTY2MWMyNDRkNjAyYWYwZjNjM2EyN2QwOTg0YzE4OTM1MDQzNjUwZGM5MzMyM2MxYWU5YjViOWM3YmEzYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE3OTQwMDAwZTAwNjM2NGZmMDM3ZjhkNWMzMDYyNzhjMjY5OGRiMTY5NzE4YWU0OWZiZTMzOGZhMWY5ZjI5Y2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxNTYwN2VkYTdmYTkxOWRlMmY2NThhMzdkMWMzNDcyMGU5M2Y3ODcyY2E1OTdiZjYxNTcxNTNmOWY4OGI3ZjgifQ" + } + ] + }, + { + "id": "claim_77569ef754954df2ba13220e12ac02fd", + "statement": "The parallel_tool_calls parameter controls whether the model can call multiple tools in a single response. Defaults to None (allow parallel). Set to False to enforce sequential tool calls.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2421-L2422", + "version": "repo-lines-v1:sha256:61eb411f416584d4ea4a194bb6e7cf28302d93bea6aa24ad3e60d1fd4d401fea:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjkxZmQ5NzQ3NDhmOWQ2ZGU1OGIwNjIwY2IxNDQ0ODE0Nzk2MWM4MGMxMTNhODQ4ZDFkYzk5Yjk5ZTdlZWQxZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNmIwNDAyZTEwZDNjOWI2OGM1ZmI4ZGJkYWFjMTNmNmFjMjgwYTgxZjUxNGZhMWQ2NmUzNTUwODdhZmYxMWU2MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTY3ZmI3OTRhZGJiYWJjMmE3ODU5YjVmOGZiNGM4ZmIxMmM1MTE2NjhlYzU1ZTc4YThhYjQyYzhkNGVkZjQ0ZSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYjEzYzZmNWE4NWFiM2Y0ZDhhYzEzMzQ4NmNiOWVmMWE4MGJhZmJlNzkxNTRlNjgzMjYyODZiMTgxN2JiYjVkMyJ9" + } + ] + }, + { + "id": "claim_53c99cf7136f4584ae5100e02b810a40", + "statement": "Streaming output integrates with LangChain callbacks; on_llm_new_token() is invoked for each token chunk via run_manager, with per-chunk metadata passed to custom callback handlers.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L1647-L1649", + "version": "repo-lines-v1:sha256:b19e7042c69c05d2f0b6b346fa80ca7ab0c988c98a431419568d5222a6992fcb:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTJmOGJhZGNjNzkwNzA4NTk4ZDRlNmI5MDc3MzY1YWJhZmI5MDVlNWEzN2Q2NTcxM2YzMTk5MGM2NzVjNTM0NCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTM0YzU0M2FkMjdjMzM4MWE1MDMyMzE5OTZjZjYyMjI2NWQxZDM5MzMzNTEwYTJkYTNmMWNkMGMxMGFlN2YwNCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZWRmMTRkNDNjMTFkNzBkNjk3Y2Y5YjcyNzg3NTAzOWYzODY5Y2YzZTI4YTIxOGFiODg2MjFkZjBhYWUyNmNiZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMzZjMWQ3MWFkNzgxZmFlMWQ0Y2UwNTEzYmZiOTE1MjYxNzNhNzI5ZTcyY2Y5YjYxZDY3NzM4NjRkMTJmNGJjZiJ9" + } + ] + }, + { + "id": "claim_edbede94a65146e0b8ff99588e7e2922", + "statement": "stream_chunk_timeout is a per-chunk wall-clock timeout for async streaming (default 120 seconds); it measures silence between parsed chunks (not inter-byte silence), and can be disabled via LANGCHAIN_OPENAI_STREAM_CHUNK_TIMEOUT_S environment variable.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L1010-L1050", + "version": "repo-lines-v1:sha256:dfa0b175a318de71dd884ca5a2fd1d06c12bf1856ac21d6a05bf7f74e67b35d7:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYwMDdhMDQwOWU5Yzk3NGE2ZWJhZTkwYjE4YjM2ZWU4MDFlY2E3MmEzNzA2NzI4MjcxM2YzNDkzMjg1YmE2M2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk1MzAxMDNlZDY0YjNmMmU4MGE0N2IyN2U3NjY4OTAzNDdlNDk5MDQwZTA5N2NjZWY0YzgxYzYyZTYzMmI5MTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA2ODc0NWVhMWI1MzViMDAwMDMwMDNkMGUyYTZlOWJmMjQ1NzBhOTBlZTE2NDFjYTE2Mjc1MzA1NTI3MDg0MDgifQ" + } + ] + }, + { + "id": "claim_86d1daa19cb746d9a2065da3c6b6ed5c", + "statement": "Vision is supported on gpt-4-vision, gpt-4o, and gpt-4-turbo models; images can be provided via image_url with detail parameter ('low', 'high', 'auto') or as base64-encoded data URIs.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L4218-L4218", + "version": "repo-lines-v1:sha256:3201e28b6cfd4728b55688cb7ad574d9f73a20f03afd7c969e7e1d597f5239cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMzIwMWUyOGI2Y2ZkNDcyOGI1NTY4OGNiN2FkNTc0ZDlmNzNhMjBmMDNhZmQ3Yzk2OWU3ZTFkNTk3ZjUyMzljZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzIwMWUyOGI2Y2ZkNDcyOGI1NTY4OGNiN2FkNTc0ZDlmNzNhMjBmMDNhZmQ3Yzk2OWU3ZTFkNTk3ZjUyMzljZCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYTEwYWVjMmM2YzZmYjBjMzg0ODI4YmQ3ZmYxZmZlMzBkN2JiY2U4YzcwMDM1Y2VkZDQ0ZGQxNjI2N2RmYjZiZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDJjNTg1YjkxZTVlZmI4MmM2M2UwMGNmOGM2YzEyZjhkM2JmNmJiYWQzZTQ5MWM2ZjE4NWQ3NmE1MWIyZDE5MSJ9" + } + ] + }, + { + "id": "claim_9f2118c4389b49648e76c95402cdfb94", + "statement": "Model profiles are auto-generated metadata stored in _profiles.py describing capabilities: text/image/audio/video inputs/outputs, max tokens, tool calling, structured output, tool_choice, and tool_call_streaming support.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/data/_profiles.py#L1-L20", + "version": "repo-lines-v1:sha256:b97089e586471f63ae9446a5505aaf38d23261256a82aaf7f8979fce8fa7291b:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJhMzc2OTFhYWUwNGYwNGE4MWY2ODU4YWVlYWFhODEyNGJlMzIxOWE4Y2JlODBlZGIwMDhiNDVmNTBhZmRlNjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlNDE2ZWUyYjAxNWI1OTkzODUzMzk0NzAzYWUyODg1Njc0ZGVmMTgyNDYwMjEwY2IyNTliMTJjOTcyNDQwOTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRjNGFkODVhNGVmZmJmMzAwZjk1OGRkYmMzMWU4ZTE5YWZjMGJmNjZiY2U1YzI2ZmUxZmFkNjFkOTI1ODJhMTMifQ" + } + ] + }, + { + "id": "claim_0deffbec52c948c1b753ce7204c5c114", + "statement": "ChatOpenAI targets official OpenAI API specifications only; non-standard response fields added by third-party providers (e.g., reasoning_content, reasoning_details) are not extracted or preserved. For provider-specific features, use provider-specific packages (e.g., ChatDeepSeek, ChatOpenRouter).", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L1-L12", + "version": "repo-lines-v1:sha256:14ebffc7bca9e6fda843b3932b482d6ffbf0487c1f74f9bfbb871bb591a6cbc1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwMWZkYTIxNjI4MGFjNjg0ZmI1ZGQ4NjIxYmE5NTJiMjU3YzhjZTVlMDExM2U2N2JjMTJkZDBlZGU1ZTg2MGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1YjZlODcwNmI0MzA0N2YyYTZkYjY3ZjEzMjYzODU5NTYwNGU2N2YzNjk2Njg3NmNlMDE1OGYwMDUzZTZiZDEifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/partner-pattern.json b/openwiki/.claims/partner-pattern.json new file mode 100644 index 0000000000..bb88fd0c28 --- /dev/null +++ b/openwiki/.claims/partner-pattern.json @@ -0,0 +1,122 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:989b7aec4a438448775477cdd98e554b1cc64e2e0ec441dc7eb1e2ea3e406d1d", + "claims": [ + { + "id": "claim_7c2a1600fd294471a122d693305b38f8", + "statement": "The _BUILTIN_PROVIDERS dictionary in init_chat_model maps provider names to module paths, class names, and instantiation functions, enabling dynamic import and instantiation of chat model integrations by name. Adding a provider requires registering a tuple of (module_path, class_name, creator_func) for each entry.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L56-L97", + "version": "repo-lines-v1:sha256:6aabf3356aa6c126fad36c4def6419dad0ea5d211ef70170ae25114005595e3b:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0OWFmODNkYjQ4ODc5YTQyYjVjNTliMmEyYmY3NThiYWY4YjFkMmQwNTJmOGRmN2VlNWNkOTIyMjJkYTM4NzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJkNzA5NGEwOTBkZDkxYjgwMmJjZWIxYWNkYzMwYWMyNTc5MjU4YTFjNjkxNTk4YWVmNjYyMDAyMWQ1NWMzMDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhYzk3OTRjOTIxODcwZGVjNjU0YWU5YjU2ZDBiMDk3YWU0NDlhODU4NzE5Y2Q3MjhkZDI5N2ZhNjMyN2VhOTYifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py#L98-L120", + "version": "repo-lines-v1:sha256:dad3c9747e8723f11fb4d86b5faa27d2165643f9857f0195c68fad27f6fb8d15:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxMDk5ZjQyOGM2MTA5Nzg0N2ZmOGZkMTdjNjQ5MTAzYmM3MDNhOTgxOWI5OWQwOTEzZWZiZDI3MGZjNjE0YzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzMDNlY2RkNGE0ZDEwZjE5YzczZmQ0MmZkMTI0MzdmZDMzNTRhODI2ODE2MDVkYTUyNTVhOTdhMzkwNjhkYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ2ODVmYTNkMDYzZDIyY2I2Zjk1NGE0OWRjZTUzMmUzMWJhNjE0NTA4NDExOTFiYmI2MmYzYmEwYjE5NDI5MDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgxOTY2MDgyOTFhZjY5NzQ1YzViMmVhZDliM2ZhYjRkMjVkZjQ2MjZkNDNhMzQyYTA3ZjkyMGRiYzhiMmJiYWMifQ" + } + ] + }, + { + "id": "claim_ede1719006f44fd88e9ed6a867a79850", + "statement": "All chat model providers must inherit from BaseChatModel and implement _generate method (required for sync generation) and optionally _stream method for streaming support. _agenerate and _astream are async variants. The _llm_type property identifies the provider.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L284-L2400", + "version": "repo-lines-v1:sha256:cdf77a3db35dd154ae26e7d5825099290e4a9d5499a0b0abe672652de89234a6:eyJzZWxlY3RlZExpbmVDb3VudCI6MjExNywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTAwNTcxZDA4NTFjMTIyMTUzYTNiMDExNTE1NTI4ZjlhNDM3MGRmZWY3YTMxZGYzNTZlOGQ3OWNjMjA4NDE1MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiN2FlNWM2NTk3ODNiYTg5NjVmNjIxOTY1MjBmNjE5ZmI3YWM2ZDk4NGI0N2Q4ODM1Yzc3NDA2OGRmZDIzNGUxNSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTU2NWNmODYzMGNkNjlkYmRiMDQ4ODQ3MWYzMzYxZjc0MzM3NTQyMDI0MWVhMDUyM2U5MzA4MWQyZmM3MWZjMiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYjZkNWFjNjk4NzhlM2NmMTQ1YmVmZGYwZjVhMmNiN2RiYWI1YzFhMGNhYWZmYmRjMGM1ZTM2OGRlMzgyYjFlYiJ9" + }, + { + "resource": "repo://libs/partners/anthropic/langchain_anthropic/chat_models.py#L1-L150", + "version": "repo-lines-v1:sha256:3f8c19788b4ef936bce0c585b292426b0a0c97ef07907d81ea0b6adc0a0603c9:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIxNTBhNDllZmI2N2JmMzZhNTk4OGExOGYwZTEzMDQ3NDBjMDY3NTRhYWIzMzMwNWIxZDE3ZTkxY2NiMWYyODhmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1YTc1MzcwOWIzNzlkYTQ5M2JkZDY3YzczZDUwMGQ2NDBhZDBlYWE2Yjg2OTYxYTAyMWVmYzA5MGJiMWJkYzFlIn0" + } + ] + }, + { + "id": "claim_ec81f5b4f21b4f2480ccacc8d1fd3ce9", + "statement": "Provider implementations translate between LangChain's unified message format with content blocks (text, images, tool_calls, etc.) and provider-specific API schemas. This occurs in _generate/_stream before calling the provider API and when parsing responses into AIMessage/AIMessageChunk.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/base.py#L93-L180", + "version": "repo-lines-v1:sha256:e6be22558dc63e5340eb68ee9c6fd5e038ce8e73a744032231edf160bf532c1f:eyJzZWxlY3RlZExpbmVDb3VudCI6ODgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YWFlYjBkNzJlNzU4NDhiYWVmMDliMGJiMzU0MDY4MjJhZmFmYTQ4YzEwMWU2NDc5OTE4Yjg3YWFiOTU2YjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQyOGVlZTRhYzUwYzgwY2I3ZTU5ZGUxNmEwZWM4MjllZGViYjRmNTJhZDkzMjk2NzNmN2JiMTA4NDBjODlkMTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRiMTc0NWI5NTA3OTM2ZTRiMDAxNGUxZDg2ODJjYTEyNzZkZWM3NTVkMjgzNDI0MTE2MTM1ZDcyNzVhMjVjOTEifQ" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L250-L280", + "version": "repo-lines-v1:sha256:686c23b74e32457db00c2e583377c31832bd2a88f13e64efdf991f0c601285c6:eyJzZWxlY3RlZExpbmVDb3VudCI6MzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxMmJjNWRjYmQxNTlmZDM5ODgzZTVmOWJiNGZhNTY1MGM3NDllYjM5ZTIzMjIwMmVhNmY2YTlhYWVjZGYzOWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBhZmNiYTE0MWY5NzhkMjA3YjYxZTRmZTAyNzE3MDYxNTk0ZDcxYTkyNTlkMTYwYjdlZDE3NWUwOGFkNmEwMDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVkOGE3YmNjNjg2YzA3OTU5MmI3ZmFiNmVlZTFkMjZmODg3NTBlMTlmMzhlNzMxNjk0NzljYjViZTQ1ZTNiOGEifQ" + } + ] + }, + { + "id": "claim_abb3b526d5bf49b19dd0d5aaf8c8b6f0", + "statement": "Streaming _stream method yields ChatGenerationChunk objects containing AIMessageChunk with incremental content tokens. It enables streaming in the API request, iterates provider events, extracts deltas, and calls run_manager.on_llm_new_token for observability.", + "evidence": [ + { + "resource": "repo://libs/partners/anthropic/langchain_anthropic/chat_models.py#L1862-L1910", + "version": "repo-lines-v1:sha256:f0e8c1f1266d35446c33b5ddf1c42ddb2d131703692ba8b427bf5a1789d82900:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkzN2Q4Mzk0YzBjZjdhYWJiZTRhODg1YjA1NGE4ODI5YWQ3NGRhYmU1MGVmYzQ5NDg3NWJjZGVmNDQyMTA5YjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgxN2VhMzk1NjdjYTc0MTQyOWI1MGRiNDg4YmQ1NDBiYzJhNmJlMmFjZDhlMzU2MjA2ZTdlYTdjOGFhNzAxYzEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQxODdkNTM4ZThhNDJlMzk2NmEzZDVhMGJmMjAxZGNiZDZkZGYwOTFkMzY3ZmY5OTJmMmNlMmQzZjMzOTU4NjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI0MmNjNmEzMGZlNjE0NGYyZjAxMzdhNzk3YzU2N2IxMmJkNTgyNWZhN2ExNGVmOTc2MWQzMzc4MDljY2M0YmQifQ" + } + ] + }, + { + "id": "claim_85d21c6e76354f61b418661763d40524", + "statement": "Model profiles are generated from models.dev via the langchain-model-profiles CLI tool and stored in data/_profiles.py with provider-specific capability data (context window, modalities, tool calling, structured output). Profile augmentations can override or extend profiles via profile_augmentations.toml.", + "evidence": [ + { + "resource": "repo://libs/model-profiles/README.md#L20-L43", + "version": "repo-lines-v1:sha256:dbedddc099ce70c40b79d35eb746c3a255a751cd7631d2e06a7b930e65689aab:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJiOTY2Mjk3MTE5MWYzZjdkMGU4NWE3Mjk1NmZkNDk0YWQ3NDAwYzVlNTNiODRkMjM0NjVmNzYwYWU4NjI4MjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImMwYjQ1MWFmZTFhMmNlMDE1YTM4ZTcwYWEwMGFkNDYxYjY2ZjNhZTEwNWFkYTgxMGI1MjRjMTExMDdlYTU4Y2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA4MWRjNjYwMWM3ZDZmYTUzNjBhMzRjMTk2NjcwZjQ1NWQwNGVjYzg3YTZhMGJkMDU3ZjU1NzFmNDI3NmQ5MGUifQ" + }, + { + "resource": "repo://libs/partners/anthropic/langchain_anthropic/data/_profiles.py#L1-L80", + "version": "repo-lines-v1:sha256:3ab056844cd9b42e3d93edeab31c0682b96f523f9d9b59282403f8276facffa1:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJhMzc2OTFhYWUwNGYwNGE4MWY2ODU4YWVlYWFhODEyNGJlMzIxOWE4Y2JlODBlZGIwMDhiNDVmNTBhZmRlNjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI1M2FhYzA0YTg2ZjQ2YzJkNDJlNmNkNDQzYTUzOTRiMjc3NTYyMTU2MDE2MmU5MzY5NTQ3NWM5MDg4N2QxZWYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxNjk2ODFlNTQ4NzRiODk2ZDVlNWVkNzczZjI2ZGI1NjBjNzFiNDQzMTkzYTNjZDc5YWRlZWY2NmVlODg0OGMifQ" + }, + { + "resource": "repo://libs/partners/anthropic/langchain_anthropic/data/profile_augmentations.toml#L1-L50", + "version": "repo-lines-v1:sha256:9458ec3ea6be6458305d952e8a58aa6d7333a82663f9b3cee0ae45caf0d101ec:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmZDM5YTg2ZGFjNGIzYzEzYjk3MWY4YWE2ZDY5NGE0MWRjMzQxZmI3ZTBjNjEzMjMwYmRjNTVhNzIzNGY2MTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNhYWEyY2RhODcxNTcyODQxMWE3NTk1NzI3YjAwZjhmZjEwY2U3Y2YxMTFlZTVlMDg3MTI4ZjRmNDBiZTYzMWIifQ" + } + ] + }, + { + "id": "claim_e28b9a02283c4117bd1f26410f1d976d", + "statement": "Chat model providers must implement unit and integration tests by inheriting ChatModelUnitTests and ChatModelIntegrationTests from langchain-tests. The chat_model_class fixture provides the model class to test; additional configurable fixtures include chat_model_params, chat_model_has_tool_calling, and chat_model_has_structured_output.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py", + "version": "repo-file-v1:sha256:dbd3bcb89b9e62550e7f3882af864dcf4a259ba3079859623e0cc95803501c5b" + }, + { + "resource": "repo://libs/standard-tests/README.md#L38-L83", + "version": "repo-lines-v1:sha256:6358c7d7376c7b54d0cc658f94b8bcf1153a79856fffa466d850d4a5176aee4e:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVhMmJmODIwNzg0MzY0NmQ4MDFjZWFhMjI4YmVlNzNiYjdiNmVhYWIyNWFkMzkyMjM3MDI0ZTYzYTIwNmJjYjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3YzA2YzU2MWQ1YzMyNzM0MzY4NTI2OGE0NmIzMzkxZjM5OThlZWU4OTBkMmJlNTFmMGQyMWNjNGE1M2FlNmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM3Yzc1NTVmOTU2OTRkMmFhZjkzYTBkMmNjZmE3YmZlMTRmOGMyZTJmZTg0OTA0MDMyOTExZTAxYjY0NGQ4M2YiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3N2ZkMTNjNGE2NmRhZGJiNTM4NTk1MTkzNjY3NzFmMjViODQ0MzY1YmVjYzdhNjU0NGM4OTM5MGQ4YTE4YWIifQ" + } + ] + }, + { + "id": "claim_a7fa4aec0f874ffaa86e9395f1870be7", + "statement": "Provider packages reside in /libs/partners/provider_name/ with a langchain_provider_name module containing chat_models/base.py, data/ with profiles and augmentations, tests/ for unit and integration tests, and standard files: pyproject.toml, Makefile, README, LICENSE, uv.lock.", + "evidence": [ + { + "resource": "repo://libs/partners/anthropic/langchain_anthropic/chat_models.py", + "version": "repo-file-v1:sha256:9831dd1180be8af8ddf618464b43d860b9b8303e9eccced056772a922fff28b1" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/__init__.py", + "version": "repo-file-v1:sha256:b69d77fd9aa09cdc548568ae5103af5800deaaf4e2742b2ca8516e99e9a3b4bb" + }, + { + "resource": "repo://libs/partners/openai/pyproject.toml#L1-L72", + "version": "repo-lines-v1:sha256:43bf650f1e56f4b3a04864c4f2739fa15737ce7403e09f4f5d7943fef7dc647b:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA0NTY5OTQ2YWM5YmJlN2NiMmNkZWMzZjhiZWNmMzQzNzUwYzU4YjliMzIxZWMyMTk1YmZiYWFiMmFhOGM0YmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjczOTU1MmZkOTY2ODc4ZTE0MzcyNjAzODUzMjhlZTZlMTY1ODBiNmEyMTQ5NTI2ZjhlOGM3ZmM3MDA4NDU5NTYifQ" + } + ] + }, + { + "id": "claim_ff627ba0a4984cd3a07a56562ca63b24", + "statement": "Provider implementations map provider-specific exceptions to LangChain's unified exception hierarchy: ModelAuthenticationError, ModelRateLimitError, ModelAPIError, ModelTimeoutError, ModelConnectionError. This ensures consistent error handling across all providers.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py#L54-L64", + "version": "repo-lines-v1:sha256:6b4cb2faf8b3c8ee096a2f1042c095b1afd407cb1493932ec8c691fe4406efa6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc2N2I4NTExMGM4ZDYwZDRmZTBhMTFlYWI2NjUwZWNlZjQ0ZTc2ZTdlMTU3ZWY0MzY0ZmQ4NzQ1YjMxODJhZDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhZmJhMDUyN2MzOTUzYzRiNmUwZTViNTczOWJhODM2MTJmODA1M2JjZGM5NDY2NzMwMTkzY2QxZDU5MGI0MDgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE2NDcxNTMyNmUwZDYxNDlkYWE3YTYzZDFkMDE3MmVlMWM1Yjk1NzlkZTliOTQ0NDBjZGM4MjFiNzVhYmNjMWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM5MzM1ZDEyMWJmMDg0ZjNkZDMzMzEzYTIzYjJkNzUzZmYzN2Q4YjRiZDdhMTgzNmVkNzAzMDE4YTQzOGIwZGQifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/prompts.json b/openwiki/.claims/prompts.json new file mode 100644 index 0000000000..869b37049a --- /dev/null +++ b/openwiki/.claims/prompts.json @@ -0,0 +1,184 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:5f946f7b6b3214808e71060f1626dc4100caf473eb5e0186d8064f20219062b9", + "claims": [ + { + "id": "claim_06ad6af0140b4318b25f43272f89e77f", + "statement": "LangChain provides two fundamental prompt types: PromptTemplate for single-string templates with variable substitution, and ChatPromptTemplate for sequences of typed messages with role-based formatting. Both support f-string, mustache, and jinja2 template engines.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L794-L900", + "version": "repo-lines-v1:sha256:2a4cbd78ddd5acc97e3eb7f736bdf8617298512ce064c7184578afae27ccacbf:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MDk4NzY1MDA1NDFjNjljMzM4OWQ5MTc5OTljMjZiZjlhZTcyOWMwMGE4NGRlNTcyMWYwMjM4NjEwODkzMzJmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhOThiMDYzZDliNDdlNjUzNzk2MjU1NzJjY2UxOTc2NjlkNTc2N2UxMmMxOGJlMDc1NjUyYjhjZDk4MWNlOGQ1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2NmUyZWFhNjlkN2JiNjkyYTU4NGQ2YzZmYTY3MTU2MzFhOTgxZjQxZmM3NmE4YjVlMTk3NzQ1ZWNhYjY3YzVlIn0" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/prompt.py#L24-L85", + "version": "repo-lines-v1:sha256:dcbbf9653bb47f119dc44c8d556acb0d4abf35d335311fa1c789b88f77f894db:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNlZThlMjU0MzM0OGNlNzQwMWI4MjlkMGM1ZTg1ZjVlNjQyZWJjZDJkNTk1MGJjYzA2YjY2Zjc1NzI2NDg3ODciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUyYjE4YzEzYzI3MTU5MmQ0MTIzZGIyNjE2Zjc4ZDk0OWU3YzBiZDA1YWQyYjlkZTM1NTlkNTZiM2UyZDUxMzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZmNDdiZmFjOWU4OGQ0YWMyM2ZiZTUzOTJhYjVmZTg5ODlmYjgzYjdkMTE0OTUxYmZiN2FjNmQyMDFiMGEwMDYifQ" + } + ] + }, + { + "id": "claim_927ac6a0c5cc4ef5ad98e2f754ef8053", + "statement": "MessagesPlaceholder injects a pre-formatted list of messages at a specific position in a chat template, enabling conversation history management. The optional=True flag allows empty lists; n_messages limits message count for token budgeting.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L53-L217", + "version": "repo-lines-v1:sha256:4c4592b99158ca55479cca728536ead0a34ce32c743c0416f002693e090ec08b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTY1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyZWU2MGYwNzI2YmVkNmQ5ZTU0MTVmNDgwNTE3YmEyOWY0YTY3ZTYzNmQ5Y2M1MTE3YmM1YzNiODYyMGE2YWVmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI4YjkxOTJmMzcxOWNlNDZhODhkM2Q3YmFhZTRlOWE2ZTQxMzVkMjM1ZjlhMjFiN2E4Nzg5NzBkMjc5ZWM4MTgxIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5OGUwZGFmOTYwNDEwZWU2ODM2MDQ5NWMwMmY4MTY4M2NkNjNhNWYxMDY1NDA3MTZhMWFmNjc4ZDRlZTgxZmNjIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIxMTY5MTgwOTI1NzMzY2FlZjYyZTdiN2VlZjVjZjA2MjEwZjQzOTMwZmJhYjkzYjVhNzc2MWI2YjczNzQzM2IzIn0" + } + ] + }, + { + "id": "claim_faf16d09f30e4545a445406bfae52860", + "statement": "Prompts support partial variable binding via partial_variables, which pre-fills template slots and reduces required inputs at format time. Partial application returns new immutable instances without mutating originals.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/base.py#L67-L72", + "version": "repo-lines-v1:sha256:fab1c8777a008cb64f2a4a121a2b07880cc9622cf7c6c9794e34bf348551b8d1:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODdmMGQwOGEzZDgwM2UwNDMyOGU5MzdhNjdkMjRjNDU0M2I1ZjM2MTRhMDg1YWQ5NjFiNGU3MjAxNWNjMzFjMCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTZhNzE1MjU3MmVlODRmMTA5MDZjNzRiOWYyYTkyNDM0ZGRhYjBlM2M0MjgxM2IwNGU1OTdlZmNlYzk1MWIyNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMzBmNzcxZTdlNDcyNWU0Zjc0OTJhZDM5OWFlOGEyOGQ3Zjg4ZjhkZjk0NmVjNTc2OWVhM2IwNjI1NTdhYWQ3NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZmYyMThjYWM1ZDk1MWI1YWQ5YWEyM2IxOTg5OTUyYTJiYjYxZTIzOWM2NzY5YzEzYzRlZjc2OGI2MDFiZjIyNiJ9" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/prompt.py#L100-L125", + "version": "repo-lines-v1:sha256:3f45cae3282d8d7c0693c92b033ec02c7d6f11880bef628ec50480c7060cd7d7:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYxMjY1NDUyNWJhMDk3NmY2NDAyYzk0OGQwMzY1ZTAyZDk1Njk5MzA0YmRkODM2YThjMzQzZjUzMmY3MzRhMDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJhYWEzNzA3NGNkY2ZmMmU2MzY0ZWQ3MDhhZjk4MDBlMjI0YmQzMDUxMzc0ZmNmMTI4YzZlNDg3MjM3ODc0YzQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU3YTg1MjA2Y2UxMWM2MzYyOWU3YjY4MjQ0NjcwYmNmNDM4Zjg0MWRlODE1ODIzOWJlOTUxMmI0NGFjM2NhNmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU2ZWJkNDlmYTEzMzI4YTFhZmUxMjNjMDZmZWZkYTFlMDM5ZTY1MjM4MWQwOTZkNDhmZjI2M2M3MGIxZTA5ZGMifQ" + } + ] + }, + { + "id": "claim_2429fe3ccdcc43d39e2f12bc1e28ac30", + "statement": "FewShotPromptTemplate combines a fixed or selected list of examples with a PromptTemplate for each example, a prefix, and suffix to create a single-string prompt. Example selection can be static (examples list) or dynamic (example_selector).", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/few_shot.py#L121-L239", + "version": "repo-lines-v1:sha256:1ed2a3f7e9232ea7dbccea5421d1ca563040b6994765fc7dfdcda7126113e36d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTE5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiMzcyZmEzN2U2MzlhYjY3NjE2NTZhYmIyZWQzMTZjZDY1ZjcwYmJkYTUxNzU3NWVlOGNjMzQ2ZTdjMTI3YjA4IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0YzE3NDdjZTAwZjA0NWZiYmZlOGY2YWEyNTU1YWQwYmVkZGY5M2UyOWQ0MGI3MWU5NThhNWQ0NjA5ZWQ2YTk2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhYjhmMDU4Zjc3ZWI4NTQwOTA2MWQ4NmIyMzA4OTM0NjMwYWUxMTc5ZDA5MDQ3ZTE0ZDM0ZDhjZmE1OGY1NzFiIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIzYmQwYzQwZDQyMDY4Yjc2ODRjZWY5N2VkYzNlY2JiN2IwZmMyMjZlNzE4MGUzNmJmMDJiMDE2YThmMGQ0ODU3In0" + } + ] + }, + { + "id": "claim_9b3c74b972a544aa8db493e82576b8ac", + "statement": "FewShotChatMessagePromptTemplate embeds example message pairs within a chat template sequence. Each example is formatted as a conversation exchange (human/ai messages), then injected between prefix and suffix messages to teach models by demonstration.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/few_shot.py#L262-L410", + "version": "repo-lines-v1:sha256:546d87960f834e65f5e8f714ed573fbc7b284cb4c13040ef4becaf2f1647ea5e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQ5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjMTdmMmNmMmM0YWI0MzYyMTRlNjBlYTUyY2E4MDQ3MjE5MWZhMDE5ZTgxNmI0YjJjOTcyNTRhN2FmNzgwMzdjIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiYWFlOGEwY2E3NmY5OGY3YjdkYzc5ODQ5NTFkNjNlNTdkYjNjNjQ1NTc5Y2E1MWJhZDZhNTM5ZDYxNGVkYmJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI4YTQ2Mjk5YmRhMDI5NWZmOWNmNWQzYzI4OGM1ZjVkMWI4ZDcwOTNkNzZmYTI1M2JjZTNiYjc3OTlhMTcwZGZiIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI3NWMzMDQ1ZGMyZDYxNjE4OTMxZGExN2I0MjNlNDkwY2RiMjYyYjJiOTUwZGZkNWUwZjY3NzdhNDJiMWE2YjVlIn0" + } + ] + }, + { + "id": "claim_121fea6f1f0d48da88bcd9b3c01b5330", + "statement": "BaseExampleSelector defines the interface for dynamic example selection, with add_example() to store examples and select_examples() to retrieve examples based on input variables. Async variants (aadd_example, aselect_examples) enable non-blocking selection.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/example_selectors/base.py#L9-L60", + "version": "repo-lines-v1:sha256:ac0c16bb2bed71d11dad21892613c5fdc4b9b471d1af2bfc6edc3c03aa426903:eyJzZWxlY3RlZExpbmVDb3VudCI6NTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIzNTNkNDkxNTA2Y2I2ZjM3MDZhYThjYWE4ZmNiMzE5ODRiMDM1YmJjZmMyMDEyMDk2OWZiYzhiNTAyMzRlMzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM4Y2VhNmI0Yjk0YTRkMWE0NjFjYzFjZDAzODQxYTViOWFiZmRhOGViNDY1MmIwMzM2Y2Y2MDA1MDRhZTEzZjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxODhlYzliYWJmOTQxYWUyM2NkODJlYmMzMmNiY2E3MDgxN2JiN2NjNjczZWFmMDA5NDkyYTgzM2VkOGU3ZGMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_35528e0c2ce64233975c8faf77da10b4", + "statement": "SemanticSimilarityExampleSelector embeds examples and inputs into a vector space using a VectorStore, retrieving the k most semantically similar examples. It supports filtering by input_keys and example_keys, and vectorstore_kwargs for custom similarity parameters.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/example_selectors/semantic_similarity.py#L101-L120", + "version": "repo-lines-v1:sha256:2c9f66ff33dcf1094b0eab237586fb1f3ecaab439b42400bd1e63bbb3f1e9e5d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjViY2U2N2ZiNDEzNzExMWMwZWJjYjA0YjdmYWYyZDllZjAzNjgzYjA4MWY0NmUwNjZjZDVkMTM4MzQ4MDc5YzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZjN2MzMmJiZjI1MjFhMDY0NTE0YWYxZWRkZGNmMTZjM2Y1NGNhYzMwNTU0YjVkZGE5MWFlZWZlYTkxOWRhOTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3ZjE4MjlhMDJjMzkwZDU1NDA1NDIyOWZlZGNkNzc5MjViYWM5YmRhY2NlZjY2OThlMjU2M2I4YjY1ZDJhYTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAyMjllZDUyNGM0ODBkMTMyMTY2MjQ5NDkyMzliNzc3YTg5Zjg2MmMyYjAwYzdiZDU1MWI3ZDE4Y2Y3MzIzMjIifQ" + } + ] + }, + { + "id": "claim_dda62be78fb24ff98ee21ecd9cf79bb1", + "statement": "LengthBasedExampleSelector greedily selects examples in order until adding the next example would exceed max_length (measured by get_text_length, defaulting to word count). This prevents prompt length overflow while maintaining insertion order.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/example_selectors/length_based.py#L18-L95", + "version": "repo-lines-v1:sha256:67421df885a91f2a4c4f7c18484470621ec0b0b161c333821e431cb6d02724e8:eyJzZWxlY3RlZExpbmVDb3VudCI6NzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNhOGIzZmFmNTExYmZlNDg4NWZmNGI4MWQxNTE3ZjIwOWRhYzg4MWUwYjY3NWQ5YzlhMTE2MjNiOTA5YWExY2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM5ZjkzMWY1NDNhODdmZDBmN2Y1ZjM2OWNiZWNiYTAwMTJlZjU2MDVkODFjMzc1Y2RhYWEzMzk1MmJmNGVmZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ5MzBhZWNlMmU5YTY5NTI2NGE3ZjdhYjdlNjk5MzBjZjJlNDZjNjJlN2I3NGUxZTg1Y2Q2ZDNlZjRiZjkyYzcifQ" + } + ] + }, + { + "id": "claim_4e0168f92b0240508b33acfa82beeeec", + "statement": "Prompts support three template engines: f-string (default, using Python syntax {var}), mustache (using {{var}}), and jinja2 (with conditionals and filters). Jinja2 uses SandboxedEnvironment to mitigate code execution risks from untrusted templates.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/prompt.py#L80-L87", + "version": "repo-lines-v1:sha256:77fc2723831e2a68d0a7b23b0042fdbb13cc1a8689c9ca608037e78cf6eefac0:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTMzZTg1Y2RmZmQyYjRmNDQ3Zjg5YzQwYmI2NmQ1ZTk5Y2E3Zjc4MTlhNzA5ZDgzZDhhZWUwNTlhNDVlM2YwMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzhkMmM0MGFmOGU3NjRiNjYzYWZmNjUyNjM3MzcxOGU5YzNhYWY4Y2ZjYmM0YzQ0ZTYyYTAwMDkwOWYwYmU4NyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMzgyMmY5MDg2YTI0MjdmZDc4YmEyZGFlMjcyOTg4ZjI2YjU1Nzk4MTQxOWViN2QxMTk4MmYwOGYzOTRkYTEyYiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNmYwZDkyMzMzYTE0MGJkNTQ1NDcwOGJiNGQ5MDYzNmZlNzhjZGFhMzI5MDA4MDc3NDEzYjk0OWYxZWUyZTg4ZSJ9" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/string.py#L30-L72", + "version": "repo-lines-v1:sha256:0dd93d64a859c73abf983816f150e2233631e7f5dff6c75a6a6e52df9249964b:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3YzA3ZWIxZTA5ZjNhM2FiNTMwNTZjYTMzNmJkZDJkMjcwMzA3ODliOWVhZjg1MjgxY2Y0MTIxZDU0NTE1NjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVlZGFiM2Y2MzY4YWY2MjQyYjMzYjg1OTgyMjQ1YWQ2ODdhMTBhMDMwNTg0NzhhYmM0NzcxMTUxOWZlNjQ0Y2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhhYTZjY2Q2MGQxMDUzYTkxOGE5ZTY4N2VlN2Q2ZWJmMDQ2YWZiMGUxNzgwODExM2JjNTVmYjc3NTkxZGMzNjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZiNGE3MjQ4ZTU2MTZkMjIyODJkNzBjNDc0ZDcxM2Y2YTE1MTM4NTJmNzZmZmQ5MDJiODdhZDg5MDFiYzQzMzIifQ" + } + ] + }, + { + "id": "claim_e9bf331f4444400c9f75560833e85a01", + "statement": "All prompt templates inherit from RunnableSerializable, implementing invoke(dict)->PromptValue, ainvoke(dict)->PromptValue, batch(), and stream() methods. This enables prompts to be composed in chains using the pipe operator | and streamed for real-time output.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/base.py#L209-L245", + "version": "repo-lines-v1:sha256:77013a1310d08ac7f53e1bab5d2ccb8a7fbf7fcba40800792702f258a0453e49:eyJzZWxlY3RlZExpbmVDb3VudCI6MzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIwZTkzZWQ1ZTBmMDYyMmU5NzAwM2E4ZjlmNDI0NjU2M2RmNGM4ZTNiYTczYmM4ZDFjNWFjNjQwMjQ2NjM3YWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcyYWRlMDc4MmY4ZDk2NmEyOTNkNjI3MmRmMjk0NWMxZjc1OGI1ZjMyNDhjZjY3ZTZkMDc4YmFmZGIyNjVmYjYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllOWRhM2FhMjgzMWI2NTg5YjhmZmMwOGQyZmVhNDBlZDU1MDMzOTkyNTEwZmVjM2M4Y2IyMTM3OWUzZDc3YTUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/base.py#L38-L106", + "version": "repo-lines-v1:sha256:cb21cabaf35887c5a33bcc003d55152713d0ef71220e67ce5760c82164483d60:eyJzZWxlY3RlZExpbmVDb3VudCI6NjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4N2Y5MjEyNjY1ODMyZTQ2ZGY0M2RkYzA3MGI4NmJkYzU1ZjZiZDJhN2E4ZmEyMTQ4NjU2MTExMjMzODk1MWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyYWIwOTBiY2QxNjIwZmE2MmVhZWUxNTdhNzRmY2E2MzVhYTI5ZTEyY2JmZmQ2MThiNTc5MmQ4N2JjMDNhM2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkyNmZhZGQ5YWM2YzQzMDJjZWNlMjkxMWNiM2VhMzkzMDcwYzBjZmI1MTZhMTk2NzMxMjllOWUwYjE0MTVjMmMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxM2QxNTY1OGFlNGYxMWMwYWE4Mzc1NWM2MDM0ZmI2MWZhMWY1OWFmMjRlNTYwOWI4NTc0MjJmOGQ3YTQyOTcifQ" + } + ] + }, + { + "id": "claim_12a19b1dd96544cea8a176d97e4110af", + "statement": "Prompts compose via the + operator, which concatenates messages (for ChatPromptTemplate) or templates (for PromptTemplate), merging input variables and partial variables. Conflicting partial variable keys raise validation errors.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L1011-L1048", + "version": "repo-lines-v1:sha256:84fc506d4c49fa14d72a9c9a5c2d4d5cef6d47fbda0488205d75f49bc7bfa6d9:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMyNDYwZjM0Y2U2OGViZjk0NjY4ZDAwYjEwZDkxZmM1YmIzZTIzY2E3NDhiN2ZjNjE5MjgzZDAwY2EyNzA2YjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJmODdkMGI3NjRkYTkyY2VjZWFiODlkZGVjOGJmZmVmMzJlMGU1NTg4ZDNkNDU4OWQzMTc0NmFmMWJjNTdlODQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNkMjk1YzY5NjZiNGRmYWFlNzkzZDI5NzQ0MjUyZWIwOGJiZDNmMjg3NDAwNzBmZTgzMjIwNGM4MzczMWJhZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE4NjYwNjQ1ZmRmN2E2NTgyY2YyYWZhYjRlMWQzZWVkOWQ2YzIwMjkwMjBkYTEzZGVlNzI4ZGRiMTg4NDU3ZjAifQ" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/prompt.py#L142-L184", + "version": "repo-lines-v1:sha256:48d6f7aadd76acbbe01e11ec5724decd61a2baf0857c6f81e0e5f87769a25c89:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI1N2UwYWEwOWE1NDE2N2VhZDIxNTcxZWUzYjRhZTk2YTEzMzQxYmM0OWY5N2RlYjc2ZDcwZDNkZjJmOTUwMGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzM2IzNjc5NjdlODc3ZWJhMmRhMTZmNDBmYjljMDgwYzAwZjM3MmNmODE2ZmU2N2MxNjczMWNmMzJlN2FhNGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ0MWMyMTE3NDNjYzlmZTkwNGU5NzFlOWU0MmUwYWYwY2I0MTQ2ZjQzOGM3NzI5OTNhMGE2Nzk3NDhmODkyYTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY2Nzg5ODU5N2E2MDA0MjNkZGNkOWQxMmU0MTUyYTU3ZDQyOGIwYWUyNmNhNTNhYzhjM2VmZTllNTk2MmU5MTcifQ" + } + ] + }, + { + "id": "claim_3a55cd174caa4dab97afe198abe6c936", + "statement": "ChatPromptTemplate supports role-based message construction via tuple shorthand syntax: ('system', ...), ('human', ...), ('ai', ...), and ('placeholder', ...) for MessagesPlaceholder. Full message classes like SystemMessage and HumanMessagePromptTemplate are also supported.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L794-L975", + "version": "repo-lines-v1:sha256:13fad3baacc253afb555aecd6de3d603600a7bd0eb9c5d3b5418960f8ab2d442:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MDk4NzY1MDA1NDFjNjljMzM4OWQ5MTc5OTljMjZiZjlhZTcyOWMwMGE4NGRlNTcyMWYwMjM4NjEwODkzMzJmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1MGRlMjU4NzE5NjE0OTI2MDcxYzhmYjE5ZmQ4NjYxNTA1NTVmMWIwMTM1NGY4Y2QxNjAzYTEwOWE3ZGRhZTQwIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhOThiMDYzZDliNDdlNjUzNzk2MjU1NzJjY2UxOTc2NjlkNTc2N2UxMmMxOGJlMDc1NjUyYjhjZDk4MWNlOGQ1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiYjM1MGNkY2JjMTViMDc4MGZlNjRlYzczZmI4OTlkY2JiOGNkODZhNWIzMzU3ZjlmZDVlZTAwMjU5N2UyYjBhIn0" + } + ] + }, + { + "id": "claim_b9a41f4b9b684bc1983959d01e632d56", + "statement": "ChatPromptTemplate automatically infers input_variables and optional_variables from messages during initialization. Optional MessagesPlaceholder instances are tracked separately and populated with empty lists as partial defaults.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L976-L1000", + "version": "repo-lines-v1:sha256:168085569c14a45d80a95c2e91f03e1456f5a77c51bc6d39496734aa3e1ba7e6:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRlNmQ5NTA2NmRlNGZhMzAwMDQ3ZjUwNjU3ZjhmZGJmNzEyYTNkZWMyNzkzODg1Mjg3ZjRiNWRmY2FkZTRjN2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2NDkwMTIxYmYxZDY5Y2MxMjAxNzBkZTQ2MzFkMmYwYTVhMWE3MjhkMTVhMTgwNTQ3MzVlM2M1ZjNkMThmNzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRkOGZmMzFkZWNkZjY5ZTFkZDkyZDVmYjNhOTM0YWQwNzYxMmViYjM0ZDM4MDEzNzMyZDM2OWVlMDIzYzI2YzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxM2QxNTY1OGFlNGYxMWMwYWE4Mzc1NWM2MDM0ZmI2MWZhMWY1OWFmMjRlNTYwOWI4NTc0MjJmOGQ3YTQyOTcifQ" + } + ] + }, + { + "id": "claim_466dc357eb8a48d5b7c341c5ca227fe6", + "statement": "StructuredPrompt (beta) combines ChatPromptTemplate with a Pydantic schema, instructing models to produce JSON output matching the schema. This enables constrained output generation for tasks like fact extraction or data classification.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/structured.py#L36-L82", + "version": "repo-lines-v1:sha256:5900ce331b77f1922e87c8ce15d17d499fd0d5523306462c98b2e5a952c46f11:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ5NDA2YWZiY2ZlNjYyMTgxOGU1M2E3OWU2NzU5MzYxNTAxNmE0Y2IyZWUzYTI2M2U5MGYwNDlmZDU4ZGNiOWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjVlMzQ2MzM4NzdiZjhjZTJkNGEzYzE2YWE5MjBiNmYyMTlmMDkzNjY3OWE1ZWJiYjZmNGJlZDQ5MTQyNjlmNmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxM2QxNTY1OGFlNGYxMWMwYWE4Mzc1NWM2MDM0ZmI2MWZhMWY1OWFmMjRlNTYwOWI4NTc0MjJmOGQ3YTQyOTcifQ" + } + ] + }, + { + "id": "claim_d409c285b83a44afb2996c8b05f17e2a", + "statement": "Prompts integrate with the Agent Factory (create_agent) via system_prompt parameter or ChatPromptTemplate construction. Middleware can intercept and modify prompts before model invocation using wrap_model_call hooks, enabling prompt optimization and safety filtering.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/base.py#L38-L50", + "version": "repo-lines-v1:sha256:25809f8099cf12bf0f306c009f72f72c86ea9af71fca489046284057b6708dce:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4N2Y5MjEyNjY1ODMyZTQ2ZGY0M2RkYzA3MGI4NmJkYzU1ZjZiZDJhN2E4ZmEyMTQ4NjU2MTExMjMzODk1MWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1NjU1MmJkYTIzMzgyNTIwZGVmNjc2ZjE4YjFlMjliYTg0MjQ4YWY4NTExOGMwYzVkZDg3ZDg2MThhZGExMDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkyNmZhZGQ5YWM2YzQzMDJjZWNlMjkxMWNiM2VhMzkzMDcwYzBjZmI1MTZhMTk2NzMxMjllOWUwYjE0MTVjMmMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI2MDAxYzEzN2MwYmUwOWM3MDE2OTcwYTg1NTIzODRiMDZkNGY0ZmJkZmI3MjVhNDgyMDQ2ZTU2OGMwNDg0NDMifQ" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/chat.py#L1-L50", + "version": "repo-lines-v1:sha256:9e7a196791d7921f5dda4aff01828a5731663f7a163f5da719a6140c2fec3dee:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdmZGRjYTFjZDMwMjFlNzA0YjcxMjNiMTYwMGIzOGM3ZDExNTRhYWZiNGJlNzEwOTA1ZTVkZDE1MjBlNzk0NTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MjU2OTVlYTJkY2RhMWM2M2JlMjg1Yjc4MDlhMmEwMzYwZGEzZGZiYWY3ZjMzYjJkMTA0NzJmYzZhMDNkMDEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgxZjAyMzJlOTNkNTcxMDA4MTM4N2E5ZWFkNmFjZmRlZTYzNGRkZWU3YzQ2ZjE0NDc3Y2NhZGRhMDZmZWE5OTIifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/quickstart.json b/openwiki/.claims/quickstart.json new file mode 100644 index 0000000000..7cdf8af082 --- /dev/null +++ b/openwiki/.claims/quickstart.json @@ -0,0 +1,188 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:7dc36350c57797f4e4cb42c4ca2523c8a6fda499d2176362da99865392c8721b", + "claims": [ + { + "id": "claim_7425c73f5e4c4667b8babba66c3663a0", + "statement": "LangChain is organized as a three-layer architecture in /libs/: core (base abstractions including Runnable, BaseChatModel, tools, prompts, messages), langchain_v1 (agent orchestration, factory, middleware), and partners (provider-specific integrations)", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/__init__.py", + "version": "repo-file-v1:sha256:fa4e54cbdf24eb2cfc4fd24776115853a9bc7977447b4d072a470f6765d2a781" + }, + { + "resource": "repo://libs/langchain_v1/langchain/__init__.py", + "version": "repo-file-v1:sha256:ca83a85cf943caecb4b09073ad78355112742393ccc133451a10def0be7cb84a" + }, + { + "resource": "repo://libs/partners/openai/langchain_openai/__init__.py", + "version": "repo-file-v1:sha256:d08e06dea1f954a477fa8951e2a8387a10396d07b92a3a60fdced307c136a84f" + } + ] + }, + { + "id": "claim_a13c1f7c4ff7487793c8a02c71997a8e", + "statement": "langchain-core (/libs/core/) defines base abstractions and protocols that all LangChain ecosystem implementations must follow: Runnable[Input, Output], BaseChatModel, Messages, BaseTool, Prompts, Output Parsers, and Callbacks", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/__init__.py", + "version": "repo-file-v1:sha256:79cb9a4b8511da9536ce64fd2d7e11e05c19154f9b144159f6eea38fd752d8c9" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py", + "version": "repo-file-v1:sha256:a2c769b852868ee63985025e519a25bb988b8c987f5958033c7c97d832a70d11" + }, + { + "resource": "repo://libs/core/langchain_core/messages/__init__.py", + "version": "repo-file-v1:sha256:86bb3118ed302c422bf35829bb670bf3d915e8f7a25b5c98f86d2b86a6185e6b" + }, + { + "resource": "repo://libs/core/langchain_core/output_parsers/__init__.py", + "version": "repo-file-v1:sha256:83930fac3f2c24af23799442202bd6469f9db40ab58eacbe7c5fb04b97a0af94" + }, + { + "resource": "repo://libs/core/langchain_core/prompts/__init__.py", + "version": "repo-file-v1:sha256:d0b87655ce1ba50f666dfa1a3d316dbda5c506bead04194888b034a5f03ef407" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/__init__.py", + "version": "repo-file-v1:sha256:3115ee61a18a70378fcdfa9a7eb3bc9cb765b7734cd481156110d711f9c41143" + }, + { + "resource": "repo://libs/core/langchain_core/tools/__init__.py", + "version": "repo-file-v1:sha256:d1bd72c189435f6935072871e69db5872570674dd36db2aa223174321d235955" + } + ] + }, + { + "id": "claim_3715231490364f3caa6ad55f21a58817", + "statement": "langchain-v1 (/libs/langchain_v1/) provides high-level agent orchestration through the Agent Factory (create_agent), agent middleware system, high-level chat model factory (init_chat_model), and LangGraph-based agent execution", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py", + "version": "repo-file-v1:sha256:2e1855c669ed50df9795b1f73ca690d7664d0ef58a1c4080d0f6504076ebea97" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/__init__.py", + "version": "repo-file-v1:sha256:51b55618fb209bce1e69f2911b367963da549d930f5eef1b1125cd19a26586ae" + }, + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/__init__.py", + "version": "repo-file-v1:sha256:8f55d904d302f3579169ce5e2ece766b2401f1a4d7064d0fefc67e9c6fd818de" + } + ] + }, + { + "id": "claim_a0e0ccef86c546278972c1bf40c74afe", + "statement": "Partners (/libs/partners/) contain provider-specific integrations (OpenAI, Anthropic, Ollama, Groq, Mistral, etc.), each implementing BaseChatModel and core abstractions for their respective LLM services", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/__init__.py", + "version": "repo-file-v1:sha256:d08e06dea1f954a477fa8951e2a8387a10396d07b92a3a60fdced307c136a84f" + } + ] + }, + { + "id": "claim_e9cccec63975419089bd76979ebd1ba8", + "statement": "LangChain monorepo uses uv for fast, deterministic dependency resolution; developers install dependencies via 'uv sync --all-groups' to get test, lint, type, and dev tools", + "evidence": [ + { + "resource": "repo://libs/Makefile", + "version": "repo-file-v1:sha256:b703a85cdfdb60e98c41ec8261d9b78f0e7137105ee9da36b8cb919b2d35858b" + }, + { + "resource": "repo://libs/core/pyproject.toml", + "version": "repo-file-v1:sha256:325931f1a0825e70a5f7c0228e9a98e3bd6e2395ea23db6d7a0dffce70087408" + } + ] + }, + { + "id": "claim_6c6c8b397aea45c8aba5ea826906c408", + "statement": "Pre-commit hooks (.pre-commit-config.yaml) enforce code quality on every commit by running YAML/TOML syntax checks, text normalization, per-package formatting and linting (ruff, mypy), and version consistency checks", + "evidence": [ + { + "resource": "repo://.pre-commit-config.yaml", + "version": "repo-file-v1:sha256:f6173b7f48573811a6a8ca9f0691ecb2d79c331ac1676af11542ab5cdcf68deb" + } + ] + }, + { + "id": "claim_889d710efaf440858ab48841e9823ecd", + "statement": "Unit tests in tests/unit_tests/ run with socket restrictions (--disable-socket) to prevent accidental network calls, use pytest-xdist for parallel execution (-n auto), and unset LangSmith tracing variables to isolate tests", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L13-L20", + "version": "repo-lines-v1:sha256:ab8103f9ee7c85d9fb1c9d3d0cbe6264d1384aac292f90c74f84c77c06ed5398:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDliMzBjZTkzZjhkMmVlNmQ2NDdlYjNhN2RhYjQ0NjRiNWQ5Yjc5ZGExOTI4ZGE2MDY4MDQ3M2QyY2Y0ZTY4MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTdmYmQ4MDQxMWFjZWNkZDkxOGI4ODk4NDE3MTg3MGY5NWU4ZjE3YTZiNDA0YmU4MDY0YmE3NmJhNjc2NDk3NyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjQ2M2RlMzg2ZTA3OTFhZGQ1MmUzZjZlM2EwOWYyYmY2NDhmZTQzMTMxOGUxOTE1YjdlNWRlOTlmN2E1ODQ1MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzgzNzQ1ZTU1YTIzYmI0MzZjYTUxMGFiMTgwNGQyODMwMjhhNTJlNzc5MjE5YmNlNzAyODRkOWY3YjEwMDcxMCJ9" + } + ] + }, + { + "id": "claim_4d72684ee1494db39c808dba3aace38f", + "statement": "Each package provides make targets for common dev tasks: 'make test' (unit tests), 'make format' (ruff formatting), 'make lint' (ruff checking and mypy), 'make type' (mypy type checking), with TEST_FILE parameter for specific tests", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L1-L72", + "version": "repo-lines-v1:sha256:4563e443cad83212eca51d09b8a3baf5bc291450d3eb93a8b177504a4a7defac:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk3OWFlOTgzNzk4NzM0OGRiYzA2NjU0YzBmMGQyNjE2MGE5MjA0OTM1NzUzMTA3YzNkOWMzYWMxMGNmM2VlZTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlNzJiZWQxNzkxNWM0N2VkMzk2NTE4ZDRlZmIwNTQyYzJkMWFhOWUyZDVhYzFlYTZiYjdjYTg1N2I0OWE5ZjgifQ" + } + ] + }, + { + "id": "claim_b3a658617e59415188fffdfb3910474c", + "statement": "LangChain uses ruff (fast Python linter and formatter replacing black, isort, flake8) and mypy (static type checker) for code quality; both are run via 'uv run --group lint' and integrated into pre-commit hooks", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L60-L71", + "version": "repo-lines-v1:sha256:29fec26c245d633f51a9dee3d2c1fc35c3faabf011e275f28cd0896f6dde2bab:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkMmM3Mzg1MjgzZGQ4ZTg0MzY2YzA5OTFlOTgyNjVlMmVlNTJmY2NlYWU3YTAyZjgxOWMyODcwNGY0NzBiYmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3NWI4N2NiNTcyYzgyOThhN2QzM2JjM2I1MDBkOTg3MzM2YzEyMzIzNDg0OGNmMjBhZWM1NWJlNmRmZWFhMjEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVhOWM1NmY4YjEyM2I3YmY0NGY4MTI1ODEwODZhOGRlNWIwY2E5NjVmYzA2MGIwMmUwNTFhZTllOTQzMDg2NTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg3YzBmYjdkNTgxYWNiNWNiYWIzZGU4NTk2NTRhOWY0NWJmNGVlMzE5N2U3Y2U4NzI0NGQ2NmM2ZjhjMjRkYWEifQ" + } + ] + }, + { + "id": "claim_5686f3853fca48b8a5dc4490e5ed9a6e", + "statement": "init_chat_model factory enables dynamic model discovery and instantiation from 'provider:model' identifier strings (e.g., 'openai:gpt-4o'), automatically loading partner packages", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/__init__.py", + "version": "repo-file-v1:sha256:8f55d904d302f3579169ce5e2ece766b2401f1a4d7064d0fefc67e9c6fd818de" + } + ] + }, + { + "id": "claim_ef277c441ad34123a521ed9902c3ff18", + "statement": "create_agent factory function constructs a compiled LangGraph state machine that orchestrates conversation flow between language model and tool execution with pluggable middleware composition, handling tool binding and structured output automatically", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py", + "version": "repo-file-v1:sha256:2e1855c669ed50df9795b1f73ca690d7664d0ef58a1c4080d0f6504076ebea97" + } + ] + }, + { + "id": "claim_a67b17fb1c4b43c5906fe3950b9e2b37", + "statement": "Adding a new LLM provider follows a documented pattern: create a directory under /libs/partners/provider_name/ with ChatModel implementation, message conversion, provider registration, model profiles, and inherited standard test suites", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/__init__.py", + "version": "repo-file-v1:sha256:d08e06dea1f954a477fa8951e2a8387a10396d07b92a3a60fdced307c136a84f" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/__init__.py", + "version": "repo-file-v1:sha256:8b94ff472e8acdf4b6145e2e1b37c13b6548624a8857866f694e12740e1c1d8e" + } + ] + }, + { + "id": "claim_d55345b5af7c407c8349ab5c22ed2ced", + "statement": "langchain-tests (/libs/standard-tests/) provides reusable base test classes that enforce consistent testing across chat models, embeddings, and tools through inherited unit and integration test suites", + "evidence": [ + { + "resource": "repo://libs/standard-tests/langchain_tests/__init__.py", + "version": "repo-file-v1:sha256:8b94ff472e8acdf4b6145e2e1b37c13b6548624a8857866f694e12740e1c1d8e" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/runnables.json b/openwiki/.claims/runnables.json new file mode 100644 index 0000000000..f518c6fde7 --- /dev/null +++ b/openwiki/.claims/runnables.json @@ -0,0 +1,68 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:2785275fb70363fa2ba7b5c6b351f08d2bb5de58d88dec14a5c66d33f670ea88", + "claims": [ + { + "id": "claim_c00f8ff82e09494a9cdfb9149dda319c", + "statement": "Runnable is the fundamental abstract base class in langchain_core that defines a generic, serializable interface Runnable[Input, Output], requiring subclasses to implement invoke() and providing default implementations for batch(), stream(), ainvoke(), abatch(), and astream() that can be optimized by subclasses.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L133-L265", + "version": "repo-lines-v1:sha256:0fc61d0018b1cfc55713f48b37f8f3976dad338a6308acca0e81f0b18db43343:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkMDY2NDAxOWM5MDdkZjg1MGVhOWUwNThmMTJiMTkyMzc0YmViM2ZjY2FiMDYxOTAzMGM2OGU2NTQ4YWQ4MTQ5IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhNmE3MTUyNTcyZWU4NGYxMDkwNmM3NGI5ZjJhOTI0MzRkZGFiMGUzYzQyODEzYjA0ZTU5N2VmY2VjOTUxYjI2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3MTgwODU1ZDUzMGZkYjU4MzIyNDIzYTZmZGRkZTlhM2IyYTRhMDk2MjVhNWRiMzc0M2U4OTdjMDUyYmJiZGQzIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJkMmJmYjM3N2Y5ZDhjNDQxODc5ZGI1MGQ2NTQxYzQzYzQ5NzQ2MWFlMjUzNWJkOThkYjBlNzM5ZDUyNDRjYWRjIn0" + } + ] + }, + { + "id": "claim_de3028e34ea94cedb9e4dba8f8100ab4", + "statement": "The pipe operator | implements sequential composition via __or__() to create RunnableSequence, where each Runnable's output becomes the next's input, with operands automatically coerced via coerce_to_runnable().", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L648-L722", + "version": "repo-lines-v1:sha256:34a7ca006ac1dd1353773e04c8863f5efe386b7e11a386c83dcd3cea17dfd17f:eyJzZWxlY3RlZExpbmVDb3VudCI6NzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiNjNkN2Y2Y2EzZTVjZWQ5MDg2M2YwMmJhYmQyZDkwOGNiY2ExOTViZTQwNTE0M2UyNmViODA3Y2E0MTRjMzYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiMzQzODU0OTA2YjUzMjJiMzJmMmU2Mzc4ZTZkMWYxNDU3ZmY4NjhmMjVkY2QxYzU2YTBjZjRmZWY5ZGFiZWYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkyMzEyOTc4NTcyNzU3NzcxMWUxOTFiNmYyZTlkMzA5NjdjMmNhN2JiM2U2Yzg3NGY5YjY4NjU4ODI4MDcyY2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMzZmIzOGFlN2JhNDNjODUzZTQ0Mzc1ZjUwMjU1ODNiYjcxYzEyZDFiZTc1NDFhNmZiZjY4NjFmNzVmZjAxOTYifQ" + } + ] + }, + { + "id": "claim_389134d684e1499c90f294fb34aed36e", + "statement": "Dict literals in chains are coerced to RunnableParallel, which invokes multiple Runnables concurrently on the same input and collects results into a dict; parallel composition enables multi-branch execution patterns.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3864-L3990", + "version": "repo-lines-v1:sha256:be21915dae483567351ce57dca00cd8c257732c9d43812faf0a8670366b47efd:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkYzdhNmE5MzRlNWIyMzk4MzkwZjZkYWFlMmJiZGE0M2I4M2Y1MDgxNjc5NzQ0YTA4MWI5MWI5YTExY2VhNmJmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI2ZjQ2ZDc0OThhMWE2YmI0OTAyZTM4ZDI3MGQyNGJhZjY3Mzg3MDE4ZjczNWFlMjZlNWUyNGJhNDkyZTJjMzg0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzYTM4OTc4Y2RjMWE4ZDc2MTIwYTMxZmY4NjA1NDVjYTQzYjFhNGVhYzllNmFiYjJiMDVkNjIxOWUxODQxMTg5IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlMDFiYmJkMDI1ZDE2ZjIxNDMzNTViNDRkNzM0NTlkNzFkMTgzOTkyZThiNzUxNjVjMTkwMWYzMTE4NGVmMTgxIn0" + } + ] + }, + { + "id": "claim_56606cb68e404393b90be1902fee254f", + "statement": "RunnableConfig is a TypedDict that carries execution context (tags, metadata, callbacks, max_concurrency, recursion_limit, configurable parameters, run_id) through chains via explicit parameter passing and context variables for observability and dynamic behavior.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/config.py#L57-L129", + "version": "repo-lines-v1:sha256:def5c5af24438aa68655c3dd7c5616745bc16d1e92d9f85b281622790264a60d:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ0NDE1ZGIxNWIwNDQyZjJiMWY1ZjNmZDNlMGVhMzIwOGVhYTQxNjRlOTJkMTU5Mjg1ZTNjOWQyZGM2OTEyZWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY0NmVhN2E5YWU3NmIzNTI0YTM4NWUzMmEwN2E1NmQwYTUyNjI1NTU0ZDAzNmI0ZjFiM2JkOTJlYzc3ODcxMDkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU0ODY1YWQ4MzExYTZlNGE5ODNmZjNhMjk0Mzk4NTIzNDNjYTkzNjhjNmExNTU1ODIzMThmMWI3NTBmMDJhOWIifQ" + } + ] + }, + { + "id": "claim_1be267e88ee646bb91d7c39ccd0ad127", + "statement": "RunnableBranch selects one branch based on condition evaluation and RunnableRetry adds retry logic with exponential backoff, while RunnableWithFallbacks tries fallback runnables sequentially on failure; these enable error handling and conditional control flow.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/branch.py#L43-L150", + "version": "repo-lines-v1:sha256:66036b2fd34abb1f90bcb822d35405a1b0ef80672749e09dd8fea59547ddb70f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJhMWIxNzY1NTk3Nzk0MDIzYzI1ZGNhOTMzMGQ2MDQ5NTQ3MjkyNzMwY2Q2MGVmZWE4YjVhZDljZTM4ZmY0NDliIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyMzljMDEzMzhmYzUwNGExMmU4ZjYxZDJjZThjMTk3ZGMyYjI5Yzk5MDI3NWY4NzA1ZTJhYTBkMjY2MjIxOTYwIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlOWMwNDUwMTVhMDNhZjc2Mjk2MzRlMDZlOWJlM2Y4OGIyNDI2YWI1YTM3ZmU5MGIyODJiZThmN2YxY2Y3N2RmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI0NzRhZTY0MjkzNzhlZWM2ZmQ0NTlkMjhjNjA5YjFjNWRjOGQ3NDVmMDhlOGRiODgxMmY3ZTkwMmU1MjQwYTQxIn0" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/fallbacks.py#L37-L150", + "version": "repo-lines-v1:sha256:a4a6c5df3334af9e15252973d89207d490c1b04eafea898dba1b33d0a4c2f2c5:eyJzZWxlY3RlZExpbmVDb3VudCI6MTE0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmY2U5OTZhMzU2NWJjY2E4MDcwNzYwNDYzMWU2OTc4ZTYzOWZkMTkyYjE4NzRmMTliNWJjYzQxYWZmNTQwMGJlIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI5NjEyNDJjNTEwNTVkODk2MGM0ZWNhMDgwMTFmOTM2ODM0NTZlMThjNjYwMTI3Y2JlMzBiMjhkM2M4YWEyMzJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJiZWI5MWQ5NDFlNTA0MjVlYzdhOGUwYTIxZDJhNTJmYzVkOTkzOTFmNzliYjdjZjhlNTdjMjZlZjY5MjRlNzRmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI4MDQ1ZjE5NzRiZjNjYTU1YmNkYzhiNDFkMmRmYjk4Y2MzZjkwOWZkOWU5ZWQ0OTM4M2JiNzJmMzNiOGNmMmFmIn0" + }, + { + "resource": "repo://libs/core/langchain_core/runnables/retry.py#L48-L150", + "version": "repo-lines-v1:sha256:f73fdde02952b636ccdbb1e4fcca3c715037e2b4c2be288670a407d561309db6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjZGUzZTljM2Y2MWE5NGFhNDczNmNlM2Q5NmY3YzAwMTI0NTc0ZmYxZGNlYWU0OTc1OGFiNTAzZTA5YzE5NDhhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlMDNhOWY1ODdjZTQ5ZDczZWQ4YTc0ZDM2YjUwMjg5ZDZlMGFkOWJkYTllZmM5YWI5ZTc4N2M4YjMyZjhmYzZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI4Njk4NWE0NmYyN2U0YTUzNmVkNzhhY2QyOTgyNjRhM2IyODZhMzUxNTcxMzk3MTQyYWQwMjc1OTNlMWI1MGRhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJhNjVhMjU5ODEyMTdkZDUyYzE5MTAxMTQwYWZhMjNmYWYzNGYwYzUxY2M4MWFlOWNlM2I3ODM1ZmYxZjdiNzE4In0" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/source-map.json b/openwiki/.claims/source-map.json new file mode 100644 index 0000000000..f7ea41a1fc --- /dev/null +++ b/openwiki/.claims/source-map.json @@ -0,0 +1,194 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:3b35861ca57c848ef54b88056e826fb29f242c92d428ce186f1ae6001df177ca", + "claims": [ + { + "id": "claim_c71edae5b0f64c8fa79c17ef1aca0f6b", + "statement": "The Agent Factory is implemented in factory.py, which constructs compiled LangGraph state machines for agentic loops that orchestrate model invocation, tool execution, and middleware composition.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py", + "version": "repo-file-v1:sha256:2e1855c669ed50df9795b1f73ca690d7664d0ef58a1c4080d0f6504076ebea97" + } + ] + }, + { + "id": "claim_0587839e54be45c8924d9154860a0345", + "statement": "Agent middleware system is implemented in the middleware directory, providing pluggable hooks for injecting logic at model, tool, and lifecycle boundaries.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/middleware/types.py", + "version": "repo-file-v1:sha256:56688206dce0334e7ca82125d7b809c23427890c234dfd69123dad038a387f31" + } + ] + }, + { + "id": "claim_3ca5cc3aa2d44f1aaa11eafd8fa56fb9", + "statement": "BaseChatModel abstract base class and unified provider interface are implemented in langchain_core/language_models/chat_models.py, handling streaming, batching, token counting, and structured output marshaling.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py", + "version": "repo-file-v1:sha256:a2c769b852868ee63985025e519a25bb988b8c987f5958033c7c97d832a70d11" + } + ] + }, + { + "id": "claim_f5a9f756fd1b4f3b843ef1551cfd2263", + "statement": "Message types (AIMessage, HumanMessage, SystemMessage, ToolMessage) and content blocks are defined in langchain_core/messages/, providing unified message representation across all provider integrations.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/base.py", + "version": "repo-file-v1:sha256:7398bb4db990af959df71dd81243e04b1a5f665602155a6f808271d910c4a62d" + } + ] + }, + { + "id": "claim_41da34bd04da40ffb6c3ac4c336123be", + "statement": "The Runnable protocol is the foundational abstraction for composable units, implemented in langchain_core/runnables/base.py, defining invoke(), stream(), batch(), and async variants, with composition operators for piping, branching, fallback, and retry.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py", + "version": "repo-file-v1:sha256:35840ef0581e472f34bb15d172ea90ed8e35aaf447a4e6d0104df97cca5dd8d0" + } + ] + }, + { + "id": "claim_de27d6d3996d46669d55a5294a6cb6a7", + "statement": "BaseTool abstraction and tool conversion utilities are implemented in langchain_core/tools/, supporting sync/async invocation, schema generation from docstrings and Pydantic models, and structured argument parsing.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py", + "version": "repo-file-v1:sha256:259da69b2ca5e171f4cc58acec2190352e7091f4d96a20158ce51feed4a78a02" + } + ] + }, + { + "id": "claim_b6a938d67cf04329abc2cce336bd6a08", + "statement": "Prompt templates are implemented in langchain_core/prompts/, including chat prompts, few-shot examples, image prompts, and structured prompt abstractions.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/prompts/base.py", + "version": "repo-file-v1:sha256:f2f608fba240b0d596889a9ca96ca1f8f4f49690bc24abd094f1b87c776c7f30" + } + ] + }, + { + "id": "claim_4a3cb18e1ffc46afbc05b276bc1dda11", + "statement": "Callback manager infrastructure for instrumentation and LangSmith integration is implemented in langchain_core/callbacks/, providing base handlers and streaming output callbacks.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/manager.py", + "version": "repo-file-v1:sha256:1f934c3718231ac3bd4213224886d00bad2c2295a8da1095cdfa400365f45a58" + } + ] + }, + { + "id": "claim_bb74dbecd28347a68d235dc352fd1fef", + "statement": "The init_chat_model() factory function for dynamic model discovery and instantiation by provider:model identifier is implemented in langchain/chat_models/base.py.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/chat_models/base.py", + "version": "repo-file-v1:sha256:7cad65a9057208ebc0fc0fe49b0d818ae4a8fd568e3e42d09bbf1d9e12555d94" + } + ] + }, + { + "id": "claim_27f57c927a0e4ae3964bc8a835fbec5e", + "statement": "Provider-specific implementations such as ChatOpenAI are located in libs/partners// directories, each implementing core abstractions and released independently.", + "evidence": [ + { + "resource": "repo://libs/partners/openai/langchain_openai/chat_models/base.py", + "version": "repo-file-v1:sha256:44b224c3be9dbc6308a585e6415624a7f5faa5cf0c168c551027ef237532563b" + } + ] + }, + { + "id": "claim_3b029c6ea6b141ea86e8c4f56420a82b", + "statement": "LLM behavior and capability profiles are implemented in libs/model-profiles/langchain_model_profiles/, providing metadata about model capabilities and configuration.", + "evidence": [ + { + "resource": "repo://libs/model-profiles/langchain_model_profiles/__init__.py", + "version": "repo-file-v1:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ] + }, + { + "id": "claim_00c705d2ed384205a4c87452106b2842", + "statement": "Model Context Protocol (MCP) adapter, tools, and elicitation are implemented in langchain/mcp/, providing MCP-based model integrations.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/mcp/adapter.py", + "version": "repo-file-v1:sha256:9a93363b3b18055d06778bf0508f63b85f382a829f9588b98257e014be45c88e" + } + ] + }, + { + "id": "claim_4debd15d45c545a798d12cf25ccfa03a", + "statement": "Schema definition and response marshaling for typed agent outputs is implemented in langchain/agents/structured_output.py.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py", + "version": "repo-file-v1:sha256:e3d5032f156d07531da71620710f0bb0231b3ba8ab241a5417759ebb810b71cf" + } + ] + }, + { + "id": "claim_a663419859ee445c871c8b614e26dc85", + "statement": "Tests are organized as unit_tests/ and integration_tests/ within each package (core, langchain_v1, partners), with shared test contracts in libs/standard-tests/.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/test_tools.py", + "version": "repo-file-v1:sha256:18a0d8474908b28ccd2940f35efbb04e43dfd9d4df93c0a1ad2f65777a165ece" + }, + { + "resource": "repo://libs/langchain_v1/tests/unit_tests/test_imports.py", + "version": "repo-file-v1:sha256:1ac3e45f3bfc2b66ab87e2b6d5902cc90570f0f207ce067c40a7943a6fadebe4" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/base.py", + "version": "repo-file-v1:sha256:62a5d5e75b59be001165917da5164c60bbdb07394b364df2ea1b949916ba3cab" + } + ] + }, + { + "id": "claim_691d6cb116a343ed92077ddb6a6078bd", + "statement": "Configuration files include pyproject.toml in each package for dependencies and metadata, pre-commit configuration at repo root for git hooks, and Makefile in libs/ for multi-package build coordination.", + "evidence": [ + { + "resource": "repo://.pre-commit-config.yaml", + "version": "repo-file-v1:sha256:f6173b7f48573811a6a8ca9f0691ecb2d79c331ac1676af11542ab5cdcf68deb" + }, + { + "resource": "repo://libs/Makefile", + "version": "repo-file-v1:sha256:b703a85cdfdb60e98c41ec8261d9b78f0e7137105ee9da36b8cb919b2d35858b" + }, + { + "resource": "repo://libs/core/pyproject.toml", + "version": "repo-file-v1:sha256:325931f1a0825e70a5f7c0228e9a98e3bd6e2395ea23db6d7a0dffce70087408" + }, + { + "resource": "repo://libs/langchain_v1/pyproject.toml", + "version": "repo-file-v1:sha256:0603b7b9a70d40e53ad2ebf8eeecab8636c471895728511b2e5685b6bd232aa8" + } + ] + }, + { + "id": "claim_458d709b604149fa8fb9639025882358", + "statement": "LangChain uses a three-layer architecture: langchain-core (v1.6.1) for base abstractions, langchain (v1.4.0) for orchestration and agents, and partners for provider-specific implementations, enabling model interoperability and independent release cycles.", + "evidence": [ + { + "resource": "repo://libs/core/pyproject.toml", + "version": "repo-file-v1:sha256:325931f1a0825e70a5f7c0228e9a98e3bd6e2395ea23db6d7a0dffce70087408" + }, + { + "resource": "repo://libs/langchain_v1/pyproject.toml", + "version": "repo-file-v1:sha256:0603b7b9a70d40e53ad2ebf8eeecab8636c471895728511b2e5685b6bd232aa8" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/streaming.json b/openwiki/.claims/streaming.json new file mode 100644 index 0000000000..2a439663a6 --- /dev/null +++ b/openwiki/.claims/streaming.json @@ -0,0 +1,118 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:a438de3bacc6da5a8e98bfd85d8e6f3a947deaf987bccf74fa6efdf166987bb6", + "claims": [ + { + "id": "claim_87d5d462354440a7bccb6dbe992c60d3", + "statement": "BaseChatModel.stream() yields AIMessageChunk objects as they are produced, checking streaming support via _should_stream() and falling back to invoke() if not supported. It manages the callback lifecycle: on_chat_model_start at initiation, on_llm_new_token per chunk, and on_llm_end or on_llm_error at completion. A final empty chunk with chunk_position='last' is yielded to signal stream completion and finalize tool call chunks.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L727-L856", + "version": "repo-lines-v1:sha256:8945f395d5d6b5430e498ed34a1247f6be3a49e373131a158b3edee84db49a81:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI2ZDAyZmNjYzVhNWFhZDlkMmZkZjc1NmZjN2ZlNjkzNDBmMWQzNzgwMTc0MDE2NjM4MzI0MGMzZWE2YzFmNDRmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxYWFjOTNhNjQzOWE2NDQxYTAyMTUwYzIyNmQ4MzdmYTI1YTQ2YzI1YTY3NzkzYjQyYjQ2YTVhMjk0ZjhkZTM4IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2MWRlNTAwZjdhMmRmNzQ1ZGFjN2NjYzYzZDlmMGFhNzNlNTRjY2EwNTc5ZmE1YjRhNzI1MjI4YzQ4MjBlMjBiIn0" + } + ] + }, + { + "id": "claim_6325590438b0489892a1599786f86f7e", + "statement": "BaseChatModel.astream() is the async variant of stream(), mirroring synchronous logic but using async/await for callbacks and AsyncCallbackManager. It iterates via async for over self._astream() and acquires rate limits asynchronously.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L858-L991", + "version": "repo-lines-v1:sha256:807ccf05f7bff67ca424c7f8b240e4b0317145e74e2c5c57b3d8f788617b6923:eyJzZWxlY3RlZExpbmVDb3VudCI6MTM0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIzNGU4YzYzMWQ3M2ZkMDAzMjFkNmUyY2IyNzUwZWE2M2VhYmVlOGIzNjc2YmMwYTEzZjMwZmQ1NDY0NjUwM2NiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3Mjc2ZmE5ODdiZGNkNTJkOTZhNjQ5YjNiNTcyYmRiMjZiOWU5MjcxYTNmMjljZjc0MTI1NDBkNzM1YjJkM2Q0IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2YjRlNzQ0ZThjZjE5MzY0OGExNzA1ZWRlZjE5N2M0NTNkNGM5NWE1YzI0MTUyNzdlMTA3NzFjMDgxOTFhYWRiIn0" + } + ] + }, + { + "id": "claim_47e4c9d9b94f4ac78098ea9e0dba4925", + "statement": "AIMessageChunk represents partial, incremental message updates during streaming. It includes content (token or delta), tool_call_chunks (incomplete tool calls), and chunk_position sentinel. Chunks merge via the + operator, combining content and tool call arguments; the 'last' chunk_position triggers finalization of tool calls into complete ToolCall objects.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/ai.py#L418-L536", + "version": "repo-lines-v1:sha256:c5c0e4e8c4ad9520a3725d13342d059511e117c85a00f583fd69aa75b13b2bb9:eyJzZWxlY3RlZExpbmVDb3VudCI6MTE5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI4ZGQ5NTNlNzZlN2IyODEyZTIwNGUwNDU0MTYyYzYzZWZmYzE4MDRkNTI2ZDlhMWM3ZTgxODhkZjkzYjMzOTBiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiMTNjNmQwNzI5ZmQ5YTM5NzhlODZlZmI0ZDA5ZjNjNTE0M2YzOWI3YWI1YjcxMzYxZTljN2E2ZWZlN2UyZjZmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzZTEyNzA5OTRiZmM2NjIxYWExNWMxMmYxMjg3MGE1ZWJiNDBhNmI4ZjUwY2YxOTUxNjBkZDRjYTIwOTc4YjE1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJjMTc3NDNiZDc2Yzg2OTIzZTczMDc0NTNiYTA3NTJiODY3MGI4M2IwYTA2M2M2NGEwZTc5NmQwYzUxOTg2MWVjIn0" + } + ] + }, + { + "id": "claim_eb8e4cb924c441dd91f783e4366a299f", + "statement": "The on_llm_new_token callback fires for each token or chunk during streaming, receiving the token string (or content block list for v1 format), the full ChatGenerationChunk with metadata and tool_call_chunks, run_id for tracing, and optional parent_run_id and tags. It enables real-time observation, logging, and application-specific reactions to streaming tokens.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/callbacks/base.py#L65-L88", + "version": "repo-lines-v1:sha256:e4bed7227dc627f01db18cbf919a34691f2f85f4e0b4ead3a90c7dc43ae5f17e:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU4MTNjMjlmY2U0NTdjZDgwNGMwZWQ0NjRjZDE5ZThjZjE5MjFkZjMxMGExNmY0MTAzMzc1NWNhZjM1NjM3ZGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJmMTA5NTBiNWFmZGI1NGU1NDc1NWM3MzNiYWI5ZTliMjc2N2JkZjBiMWJmNjdlNWY5NzUyMzY3YjVhNDg3ZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU1NGJmYWQyNTY4MDMxOGIwMTEzYzljYWFiZTA1MjczOTc2YmQ2NDMyMGI5ZmVhMTM5ODNjOWJkY2ZkNWZiOWYifQ" + }, + { + "resource": "repo://libs/core/langchain_core/callbacks/streaming_stdout.py#L50-L60", + "version": "repo-lines-v1:sha256:eefb74b5ea9fce9d2141392376bb51d0941bb80972240b0e1c64ea016255ed32:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU4MTNjMjlmY2U0NTdjZDgwNGMwZWQ0NjRjZDE5ZThjZjE5MjFkZjMxMGExNmY0MTAzMzc1NWNhZjM1NjM3ZGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM2YWRmMjIxY2ZlNjYwYTRmNjAzNDYwYTQxYzRhNjBjNmUyYzk5MTc4ODY4MjA5MmRiOTkwODNjMzRiODEzNTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ2ZDU3MzAyMjFjOGZjOWNlZGE5YzE2ZTI2NDNkODRmMzQzNTFhZDZjNGY2NDNmODM5ZGJkZGIzYmUxNzBmNGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkNDAxNDM2NTliMjAyODQzOTYzOTY1YjRiODE1ZmFhYjYyMzcxYmM0NDRjODk1NmY2NGM3YjI1MGQ3NmFmMTAifQ" + } + ] + }, + { + "id": "claim_aff42106f80849fabc5d804cd0fbcff7", + "statement": "Runnable.stream() by default yields one full output from invoke(). Subclasses that support streaming override stream() or transform() to yield chunks. Streaming is not guaranteed for arbitrary Runnables unless explicitly implemented.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L1194-L1235", + "version": "repo-lines-v1:sha256:52f3933957d94c8162dfa849e70cd335d61247108917a8fb081a31fe43b0f570:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkMDJmY2NjNWE1YWFkOWQyZmRmNzU2ZmM3ZmU2OTM0MGYxZDM3ODAxNzQwMTY2MzgzMjQwYzNlYTZjMWY0NGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRmYjIzYTg5OGMzMjkxNWQ3OWNiYzVkOTAxYWYyOGY0YzI3MjUxZDM3Y2RiZDA5OTlkMWZlMmMxZjkzZDFlZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImYzODc3MGYxM2U3M2YyMDU4NWIxMTU0ODRjYjY4MGY0YjI1MzlmMTU4YTExNmIwMWZkYjY4MDAwNjNlMmNmYjYifQ" + } + ] + }, + { + "id": "claim_fe26c149726c47dfa995e7b3ddafd04f", + "statement": "RunnableSequence (a chain created with the | operator) automatically supports streaming if all components implement transform() or the last component streams. If any component lacks transform(), streaming is blocked until that component completes. Multiple blocking components create buffering points but preserve streaming output from the last component.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L3075-L3320", + "version": "repo-lines-v1:sha256:ad25efc0b9dde129f68999de4a00b228f4ebcdb5e3c41d1f5d86a71690598a81:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQ2LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiZWUyODI4YWI5ODNjODVkZTdkOWUxOGNlZjVhYTQzZGY0MTJiYmRiY2FiMDc3ZjdmMDEzYjUzZWQ3ZTRlNzM3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlMGFjYmYwOTdlMWU4NjZmNGJlMzE5MjU3YmI0OWQ5OGQ1NWQwMWY4MTcxN2IzYzIyNTMwNDQ5NWYzNDMwMjZkIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJjYmVjNzljZDAzZDA2YmE2ZmU3MDBlMDhiZDA0ZjFmMDA0ZDFkZTg0ODMzMzMwN2U2ZGE5YTJlZjcyZTZmODZmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIxODQwYjI2ZmUzZWI2OTY1MDNhZDY4ZmJmMTg5ZmU4ODAwYjQ3NzRkYmYzZGIzYWZmM2Q4YzBjMTA4NDg1ODVlIn0" + } + ] + }, + { + "id": "claim_694f1031f2f64dfdb7673a21f795434e", + "statement": "The _transform_stream_with_config() helper manages streaming with callbacks: it tees input iterators for tracing, fires on_chain_start, invokes the transformer function with streaming input, accumulates outputs, yields chunks immediately, and fires on_chain_end or on_chain_error at completion. This is the core machinery for streaming through Runnables in a chain.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/runnables/base.py#L2502-L2599", + "version": "repo-lines-v1:sha256:b4db1de783dda022373ec076b06a6fc67d0a8abca8fba21251eeeef2822d5116:eyJzZWxlY3RlZExpbmVDb3VudCI6OTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUyMThjN2ZjODgzMTdhMDg1YzYxM2MxOTYxNWRkMTc2YmFjZjFhNDMyOGNjMmVkNmVlOWMzMTNiMDQ5MjQ3ZGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzZGNjMDM2YWU0ZGYyMmM0ZjM3ODgwNzVmODhlNGE5ZTc0M2ZkMGE0ODYzMTFhMjI1OTY2NDU2MWIzNTg3NDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFjODE4YWNjOWI4NGY1YjEyMGIzNmMxMjQ0MzQxMTI2NTIzZTVmZWU5NzE3MmVjNjU3MWVjMzc1NjRiNmE0YjgifQ" + } + ] + }, + { + "id": "claim_0888246b7f1c4834a3af1a55fc2f1009", + "statement": "invoke() waits for the entire model response before returning, consuming no intermediate memory but blocking responsiveness. stream() yields the first token as soon as available, enabling real-time feedback in interactive applications. The model produces tokens at the same rate in both cases; the difference is when tokens are delivered to the caller.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L474-L856", + "version": "repo-lines-v1:sha256:18b246d4f9105712e54309d31157eca8caf0cf172c8c99b1e0a1b8d2d6111527:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0NGRhOTM1MDAxOWRjMWU2Njc0NTI2NDM4NzMxNjE5OTcyMjVlOWNlNjAxMDdhZjdiNjRmODQwZGRlY2ZkYTRmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0NGFjMjA5ZDY3MjgzNmE0YzNkZDBlY2VkMTJkYzBlNmI2Zjc4ZDM4MWZmNTE2OGI3YzA3YTg0NzVkYTE5YTM3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2MWRlNTAwZjdhMmRmNzQ1ZGFjN2NjYzYzZDlmMGFhNzNlNTRjY2EwNTc5ZmE1YjRhNzI1MjI4YzQ4MjBlMjBiIn0" + } + ] + }, + { + "id": "claim_b15ce025491a4425b4d7ee2dc533063a", + "statement": "During streaming, if an error occurs after on_chat_model_start fires, on_llm_error receives the exception and a ChatGeneration merged from accumulated chunks. The exception is then re-raised. This allows callbacks to observe partial results before failure.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L833-L856", + "version": "repo-lines-v1:sha256:1c19e5928299e042429af5deae6b48b2c15aa0d928987771b51003c4f8b8b683:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFmZGFjOTI2YWQ1YTRhZTVkZWQ5M2E0ODljOTYyZjlmYzY0MzVjYzMzYWQzM2ZmN2Q5NGM5Y2VmMGQ5YjEwMjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE1NzY2NTEwODIxNzc5YmI2YTExMDg4Yzc4MjMyMWY2Y2Q1ZThhMDI1YTRhNmM3YzE4NDE5ZTVhNGQxZDJhNDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYxZGU1MDBmN2EyZGY3NDVkYWM3Y2NjNjNkOWYwYWE3M2U1NGNjYTA1NzlmYTViNGE3MjUyMjhjNDgyMGUyMGIifQ" + } + ] + }, + { + "id": "claim_6019aef1fe5d4c46b2d814b6739259f9", + "statement": "BaseChatModel.stream_events(version='v3') returns ChatModelStream, which exposes typed projection properties (.text, .tool_calls, .usage, .reasoning, .output) that accumulate protocol events. This is distinct from token streaming and fires on_stream_event callbacks for each protocol event, providing detailed event-level visibility into streaming operations.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/language_models/chat_model_stream.py#L1-L45", + "version": "repo-lines-v1:sha256:9b0e7fb5e40ee88f22f766937245805b9ed747d105421138da2b46fe8f8522d4:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ1MTcxNTdmOTc5MDY1N2FmMGViZGVjZjNjM2ZmMTA4ZTQ2MGNhYWEzMjQ5MjczOGIxYThlMzU2YTI2MGE3NTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE1NmRhNjZiNTVjODU4ZjIxZWNkYmFlOTM4MWEzMTkwNDkxNTc4YjEzZmJkNGU0ZjRlYzU3NTQyYTU5N2NmODciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNhYzM5ZDU1YjFjNDA5YWVlN2MzMThhZThmZWU0ZjEyOWVlODM1OWViMjczNjU3NWJlYmI4MDcxN2E1Mzk2OWQifQ" + }, + { + "resource": "repo://libs/core/langchain_core/language_models/chat_models.py#L992-L1007", + "version": "repo-lines-v1:sha256:585db4f95df0e78e7e568ef485dc6bf2f5aa659e705be1a76753572e91abb57a:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMxNWVhMmUwZjNmYzQ0NzAyZTQyYzI5OWZlOTE5N2FkMWZjYTgzMzk2YjdlNjMwY2ZlMmZlYjdiNjU4N2M5YjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3YmNiZjAxZjg5MGY4YzljZDUwMDA0NTlmNWY3MzRiOTFmMmZmNGRjNjVhNzU4ODlmYTFhMDIzMTU1YzZiYjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdkZGI2Y2QwZDM5Zjk4NzEyNDZjMzEyZGUyOWQwMTQwMGYzZTk1NDI0YmVmZWE0NDQwNjEwYWY3OTVjMDI5MzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY4NTE4MTRlMGExODA3MTFkMzYyYmMzYzA0YjdiNTVmMTNlNzQ0ODFjNGQzNTUwZGE1YTA2ZDE0YTc0MWFhZDkifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/structured-output.json b/openwiki/.claims/structured-output.json new file mode 100644 index 0000000000..9e8a623104 --- /dev/null +++ b/openwiki/.claims/structured-output.json @@ -0,0 +1,202 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:94885a8043502300eae424ea3f016be44eb925bbaf0ad6297fdd741c3280ee69", + "claims": [ + { + "id": "claim_150a5dc8a9c14fc19c3597a746649ba0", + "statement": "Structured output is a mechanism that constrains LLM responses to match a JSON schema, implemented via tool-based, provider-native, or automatically-detected strategies; parsing and validation occurs after model invocation via TypeAdapter or JSON deserialization.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1010-L1040", + "version": "repo-lines-v1:sha256:4883fa8d811b089eb3e2165b55a49c8c85e90aedbe15ccefeb0941ddab887303:eyJzZWxlY3RlZExpbmVDb3VudCI6MzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ2ZTA0NTJmNzY0NWFhYjhiN2UxZDg5NTk5MTVmOTc4NDRkODIwOWVmOTJiZDVmZjQwNDE1Y2QwNjc4ODBmNWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjMmEwZGZiNjkyOGUwOTYzZGEzM2JkMTlhZDIxNzg3YzgwZGMyZjkxYjNhZDIyOWM2MzFkZmY3MWQ3ZjBmZGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIyNjM3MDgzNzA0N2VkYzNmOTZlNTY1MjI5NjZiZWFhYTExYjM5YTEyYTg0ZWZmZWZjYWNhMWFmN2ZmNWQ1ODIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE2YWE4MTIzZjM5ZDM4YTNhNWU5Y2IxODk3NmUzMmMzNTBkMmI0YzE0NzUwNzIwZjNmZWNjMjQ2NzBmOTE4ZjMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1194-L1296", + "version": "repo-lines-v1:sha256:42ca8ea665535b713f1ff2651387e04941608ec55a949c4f1cf00b8febc38c66:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyYmRmNGQzMWNjY2QxYzBjYmY1ZWRkZjNlZWMxNDZlNDUwYjExMTdhNjJmZGExZDlhMzBkYTBjMzI2YTI2YTJiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyYzkyOTk3NTc2NmYwNzM5NTNlNTA5YzIyYjI0ODBiNjE0NjQzN2FhZmIzMmYyOWJkNTJjMGM4M2E1Y2ViZjU5IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmZjc4ZDc2OWUzNzBlMjA1NmU3MmI2YTU1OTkzMWIzYzVhODNjMGE2YWI2YWFmYjhmZTkzOGE3NTY3Y2Y4ZDNkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5ZjgxNDM1OTIzMTAwNThkODUzZTY3NzM5YTExNzY2NTkyNjdhODM1MTIxYTc0OWI5Nzk1ODU2M2UyZWU3MmVlIn0" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L1-L30", + "version": "repo-lines-v1:sha256:693412cf6c327d186daf9729527ef849ad6964bbd168898f4a8acb1a55d5cd05:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcwMWNiNjk2YWNlYjJkZDE0NTcyMjdlZjljZGJjZjgzYWM2NTgxYjhlYjgwMWU1OTFjZDc2OTdjM2M1MzI2OTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVjZTU5N2QxNTAwMTVhYTg1MWFmNjdkZTllNmI0ZDk2OWYwOWI5ZjE5ODVhYzBhODAwYTA3MmI2NDQwMjZkY2UiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU1NDU1YzM5NzYyNGE1ZTUxYTMxYTAzNDM2NTVjOTk1YmNkOTUwZjEwY2JkODVhOTIyYmM1ZjdiYjFiOWIxNDgifQ" + } + ] + }, + { + "id": "claim_dd15e4e31633414d9b7bd9464173ee1f", + "statement": "Three response format strategies are available: ToolStrategy (converts schema to artificial tool, forces tool calling), ProviderStrategy (delegates validation to model provider's native API), and AutoStrategy (defers selection until runtime based on model capabilities).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1357-L1371", + "version": "repo-lines-v1:sha256:effe05ad891c32593f450b159ca6b9be43e82e647f5b20d3f881dd1e19397d2d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZhNGVkNjJhMWI2MTAxZmQ4ODEwMDA5MjY3MjY4MGFlNWY4ZmUzZGY4YjFjZjVhZDk5Nzg4MDllOGQ3MjZmNjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNiYWY5MGU2ZDFlZjIxMjViNTk4NDE5YWU0MWVmOGQ4ZDRiZTU1ODkyNmE2Yzc4ZWE3YWU1MThlNmRhZjc0YjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ0MzdhYTE2ZGIzNDFjYTYzODYyMDk3MjYwZTk1YmQyNjhiZjFmOTRlODEzNzY1MzA4OWUzYTIyMTMyOTQwODgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L195-L268", + "version": "repo-lines-v1:sha256:ae3d02df362c23e8591cd85cee5c99b563537b965a3858bc1ea0f60bf6729a2e:eyJzZWxlY3RlZExpbmVDb3VudCI6NzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5YTRmMGQ5MjI5ZGZjYTRhYzUwOWU0NzQ5ZDI0YWJkYjEzODY0YjBhZWYyYjE1ZWJiZDk4OWQ1ODI2OGNkNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdjZGUxODBiYWZlMDUxZjUyMGMxMGJhZDdmODg1YzIxZjA4MWI5OGU3NjMyYWU0ODEyNWM5OTA4NzNmOTNhMTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg2Y2Y2Y2M1MTRjMzY1YzJjN2FmYjIzMDI0ZGJhY2Q4YTUwNzY2ZDI2NzZiNDU3MjU5MDdlNmYwMGI4MTkzZWMifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L270-L315", + "version": "repo-lines-v1:sha256:14177b9e9910690b3e94ebb730f43d4b82948d60529b33fa060f50c828ea596a:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5YTRmMGQ5MjI5ZGZjYTRhYzUwOWU0NzQ5ZDI0YWJkYjEzODY0YjBhZWYyYjE1ZWJiZDk4OWQ1ODI2OGNkNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM4MzBlMTZiMWMxYWFmODIwNmY1NmM3NmM2MmE2OGQ3ODY5ZWM2MjY0MjZlYzI4MjdiZjQ3ZWRkZGUwMGRmZDYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI4ZjY1Yjk4ZDlhYjA5N2MwNzExZGIxMzYwN2RhNDJlZmRiZjI5YThiNmVkYjdlMDAyYzM4NTg5ZDZlNjUzODcifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L457-L472", + "version": "repo-lines-v1:sha256:d14862e850ca6d067c9a914b6b4e407915172e3643735c814bccdf2ed76fffc5:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiZjA5NDJjNTBkOWIzZGExOTk1MDczYzlkOTA4NjZjOGMzMGJmZjc3MzJlYzNlMWNmMGFiNDk4Yzg1OWZjZDQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgwN2RkNjc5OWVlZGRhZmI0Yzk3MjU5M2Q4NGEwYmFlN2JjNWEzNjRmNjVlNDVhY2RhYTIxNDY3NWYzMDc0ZTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEwMDVmMzRlYmNmY2ZiZTZjYjNjODAxODg3YmJjZGQyYTJkYzRhOWRhN2YzMGUwYjM2YzNlNzJhNjdmZTBiN2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_131ad8c43ffd427ca47673533b2e53ee", + "statement": "ToolStrategy wraps schema as StructuredTool with JSON schema as args_schema, adds tool to model with tool_choice='any', parses model's tool call arguments via TypeAdapter, and returns parsed result in structured_response state with optional validation error retry.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1032-L1036", + "version": "repo-lines-v1:sha256:3ec24b486f5df997997f29facefed0ae53e4695227ede4eabf041adcb06aaf36:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYWQ5ZTQ5ZTU5NWYyMmRkMDg3ZjAwMTAyYTU5MTAxYzYwN2VmODJiZmQ3ZmViYTg2NmZkYjFhYWUwMmYxZWIyZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTIyNmE0MzMzZjdmZGY4OGFiY2I5NDhjOTZiZjgyNzllZDY3MThkNjE1NWVmMWZkNGIzNDYyN2M3MzJhMTBkYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDg0ZmFiN2U1OGQ4ZDYzYzUzOGNkYjNlZDYyZjdmYWI0NjZiMDY5OTFhMmQwMGQ4MjY3NjdhNGFhODJhZTIzZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTBkN2Q2NjIwMjg1OTU4MjljZGIzZTg5ZmVmMjlkNzMyNjFjZmQ1NWVmZTE1N2M0MmZmYzE1ZDVlMDZmNDQ5YSJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1222-L1294", + "version": "repo-lines-v1:sha256:985117da091c7dfdb4e1d1a3e0b1f3128312043fddd281707bcc47d29a24bc2e:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM3OGFiMDE1YTZkN2FhZDQ2MWM3NDgzODFjOTkzODIxMjg0MTRiZGYzZWRmNmNkN2Q1YWZkN2IwNDY4YjExNWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkYjMzOWMxN2Q0Y2M1NGU3MGM0ZmNmZWNiNjExYmI5ZTdkMzYzZjI1YjE4ZTA5MjNhMWQzMDZjYzk1OTI0ZTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjViYTg3NWUwYTNlNzlkYTkwZjdiY2ZhYzYxOWU1ZjU4MWM4MmEwYjU2YzUyMTI3Y2E3YTdmYWM5NzJiNDIxMDYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFmZWQ2MTVlZmExMWYwYTYyZTBiZmQ2MDZhZDE1NmM3NWU2NTYxNTBlMWYyZmNlODkwZjgxYjVlMjk1YWY5ODUifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L317-L370", + "version": "repo-lines-v1:sha256:9cf6911a903241dab87c21a3a65ca9d974d31481aa878640386d02218f3c6bf6:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFmZTRkNjc1MDQ3ZjBlZWY2ZDlkZmU2YjJkMmE1OWNlNDM0NmQzMmIyYThmNzM5YjdiYWFmZmU1MGM0MmViMTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyZDNlYWY4NWNhOGZkMGI2OWM5NjFjNjJjNDM4MmNhZjAwY2M5Njg5NTRhNTVhY2EwOTJiM2RkZDdlZDQ4NWMifQ" + } + ] + }, + { + "id": "claim_e29c5860257747bd971341c989fbb2ef", + "statement": "ProviderStrategy sends schema in provider-specific format (e.g., OpenAI's response_format with json_schema type) via model.bind(), receives JSON response, parses via json.loads(), validates against schema via TypeAdapter, and stores in structured_response; no validation retry occurs.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1205-L1220", + "version": "repo-lines-v1:sha256:d08adb0beb5d1f3445e47357718cab2751dc93ad5fa34065440339646407790e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzNzQyYmE5NDI5N2Y0NGZkMzU3YTlkYWU2NzllZmVjY2NiNzE4NGNhOWY2ZjExMTFjMjdhMjM0OTcyYzZmZDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5OWY4YTdmNDhmODAyNDQ0YzlkOTZmYzQ2YjgzZDRlOTYzODgwNDJmZjkyMzQyZGNjOTIxYWU5NTRkNGQyNDkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY1YTFiNDhkZDY3NGQ0NzUwOGY0NTcwMDZhNzM1ZDY2MjIyZWMwZjc5MzE1NTdkNDQ1YzczZThlMzZjMmVhZTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3NzI5N2M3YWQ1OTljNmExN2JhOTFmNzQzOGU2ZTJiMDA2YjFmNzE4OGFhZjc5NzRjNjNhNTIzYjIxYTNkYjcifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L295-L315", + "version": "repo-lines-v1:sha256:53e1c1b06cbbf41080d2f073f84b2eb1edfdc68e8cd26db7a2fc2302974be904:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4YTliNGE1YjkxOTBmYTUzNDllZjJjY2EwMTFlOWM4NDI2YjEwY2E4M2MzY2ZiMzZkODgyZWFlNGMwYTlmYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU2NjUxNjRiZDhjNDc4NTZlMjcwMjVlMWEzNTNkOGJlZGQzNzIxOGYxYjk5MjBlN2I4ZjIyNzZjOWM2M2Y2MWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI4ZjY1Yjk4ZDlhYjA5N2MwNzExZGIxMzYwN2RhNDJlZmRiZjI5YThiNmVkYjdlMDAyYzM4NTg5ZDZlNjUzODcifQ" + } + ] + }, + { + "id": "claim_139468bfd71741bc8502700f319767ac", + "statement": "Supported schema types are Pydantic BaseModel, dataclass, TypedDict, and raw JSON schema dict; _SchemaSpec normalizes these by extracting name/description and computing JSON Schema via TypeAdapter; dict schemas skip validation.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L106-L193", + "version": "repo-lines-v1:sha256:97b580993fe9832e499db71338047965638ba04d553de9b102698fabb6d87952:eyJzZWxlY3RlZExpbmVDb3VudCI6ODgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5YTRmMGQ5MjI5ZGZjYTRhYzUwOWU0NzQ5ZDI0YWJkYjEzODY0YjBhZWYyYjE1ZWJiZDk4OWQ1ODI2OGNkNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc5OWJhM2QxMmYxMDk5NTEzYzhjNGE2MTY1N2E3MjUxNDFjNDViMTEzYWJlMWQwNjIyMzkwMjVmYTRmMDM2YjIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUyZTZlN2MxYzk4ODIyZmM2NzhkMzMyM2E3OGFiNjgwMDNkZDQ0MmZkYjViMjQxMjEzZDBmMGYxNDYyMDJmYWYifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L77-L104", + "version": "repo-lines-v1:sha256:9650b8d7835c0edc52ceb1fa347de6e492f1c2e2aee23ba02a039bfdcf38495e:eyJzZWxlY3RlZExpbmVDb3VudCI6MjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0NGIzZjliOTY3MGNlYzg0MDM0NGY0NjYyM2IxMjkzMDhkNTUwYzFiNTQ2YjllZjg4Y2ExYjNlOTg5ZjVkNWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE2MzE2MjEwYTE2YTEwMWU2MWIyYzUwZTRlNTM0NDc5ZmQ5NTUyNDZkMTI2NTM2ZTI2NDI0NjllYWNlOWQ3Y2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU0NzMwOTFhZWEzMDVkNjE4ODEyNzkxNGNjOTBiMjliNmI0YjVmZjJhN2E4YzIwYjEzOTc5YTdhN2Y5NjYzNzgifQ" + } + ] + }, + { + "id": "claim_a927f85cb80244c8a5d55db362b6757d", + "statement": "OutputToolBinding stores mapping of schema to tool: original schema, schema_kind classification, and StructuredTool instance; parse() method reconstructs original type from tool call arguments using TypeAdapter or returns dict as-is for raw JSON schemas.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1032-L1036", + "version": "repo-lines-v1:sha256:3ec24b486f5df997997f29facefed0ae53e4695227ede4eabf041adcb06aaf36:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYWQ5ZTQ5ZTU5NWYyMmRkMDg3ZjAwMTAyYTU5MTAxYzYwN2VmODJiZmQ3ZmViYTg2NmZkYjFhYWUwMmYxZWIyZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTIyNmE0MzMzZjdmZGY4OGFiY2I5NDhjOTZiZjgyNzllZDY3MThkNjE1NWVmMWZkNGIzNDYyN2M3MzJhMTBkYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDg0ZmFiN2U1OGQ4ZDYzYzUzOGNkYjNlZDYyZjdmYWI0NjZiMDY5OTFhMmQwMGQ4MjY3NjdhNGFhODJhZTIzZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTBkN2Q2NjIwMjg1OTU4MjljZGIzZTg5ZmVmMjlkNzMyNjFjZmQ1NWVmZTE1N2M0MmZmYzE1ZDVlMDZmNDQ5YSJ9" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L317-L370", + "version": "repo-lines-v1:sha256:9cf6911a903241dab87c21a3a65ca9d974d31481aa878640386d02218f3c6bf6:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2ZmJhZTBiOTFkNDRmYzBkNDlmNWRlNjkxZGE2ZmM0MzFmMjYwMTM0YmY3MzFlMGUyMWY3NjQ4MDk0MzM0Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFmZTRkNjc1MDQ3ZjBlZWY2ZDlkZmU2YjJkMmE1OWNlNDM0NmQzMmIyYThmNzM5YjdiYWFmZmU1MGM0MmViMTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcyZDNlYWY4NWNhOGZkMGI2OWM5NjFjNjJjNDM4MmNhZjAwY2M5Njg5NTRhNTVhY2EwOTJiM2RkZDdlZDQ4NWMifQ" + } + ] + }, + { + "id": "claim_5bb393468624409b8ec58f0401f8fa8e", + "statement": "AutoStrategy defers strategy selection to model invocation time; _supports_provider_strategy() checks model profile for structured_output capability and model name patterns; if supported, ProviderStrategy is chosen; otherwise ToolStrategy is used (tools pre-built at agent creation).", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1357-L1371", + "version": "repo-lines-v1:sha256:effe05ad891c32593f450b159ca6b9be43e82e647f5b20d3f881dd1e19397d2d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZhNGVkNjJhMWI2MTAxZmQ4ODEwMDA5MjY3MjY4MGFlNWY4ZmUzZGY4YjFjZjVhZDk5Nzg4MDllOGQ3MjZmNjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNiYWY5MGU2ZDFlZjIxMjViNTk4NDE5YWU0MWVmOGQ4ZDRiZTU1ODkyNmE2Yzc4ZWE3YWU1MThlNmRhZjc0YjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ0MzdhYTE2ZGIzNDFjYTYzODYyMDk3MjYwZTk1YmQyNjhiZjFmOTRlODEzNzY1MzA4OWUzYTIyMTMyOTQwODgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L560-L605", + "version": "repo-lines-v1:sha256:5597a2118ceefb9335a013a037ae3e915f63d766f22ac599754aa4ee792dc3ec:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRmOWU0YjE2Zjc0YTRiNzA0ZWFlN2Y0MjUwZDZhOTU0OTkxYmQ4ZTZjNThmYzdjYThlYTMzNzJmZGQ3ZGNmNzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdhMDdiNjdjZmI3YjEzMWE1ZmNjYzgwNTY3Mzc3YTY5MzJmNzM5MTM3MjgwZDgyODU2ZDgxZGQ3OGMxYzdlZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMyN2U2YmMwMDFhZjczNjE3M2YxMzZjNTkwODg1NGQzM2YxNzQ3NmZlNzJmZDczMDQ3OTJkNGM0YTI4Zjg2OWYifQ" + } + ] + }, + { + "id": "claim_95984a54e631494e9326e91624899d8b", + "statement": "StructuredOutputError base exception stores AIMessage; MultipleStructuredOutputsError raised when model calls multiple structured output tools; StructuredOutputValidationError raised on parse failure; handle_errors parameter (True/False/str/exception type/callable) controls ToolStrategy retry behavior.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L628-L656", + "version": "repo-lines-v1:sha256:00fd5f0b9df0762ae54ede2d60f5bbeb785b3055f04eeebf87c015e0171088de:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZmM2JjYWUzMzhlZjQxNGQ2NmFhM2Q4MTUxNjA5MmE2MWMxNGZhNTllZGExMTMxMmVlMzgwM2UwNDNjZDAyMzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA5ZjViNTk4NTM5MWU3MTAyMWY1ZDZiYjczZTE1YzgzZDYwNmJmNTNhMTVhYzM1NGRhMGI0OWM3YzZjYWZlM2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM5OWMwMTM5N2U5ODE1NGMzY2ZmMDZlZWY2MzhmNjAwODc0NjA1NTA0OTUzNTJlMmQwMDFkMTU4OThmYmUxODEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L210-L231", + "version": "repo-lines-v1:sha256:e252cc76fcf4722f8c58272db6776a145dacaa594e39c861920f3a1ca4681b40:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE5MTYzZmIxYWZmYmZjMzdhZTRhYjBhZmI5YmVhNmM3OWJmNTc3NjRmNmRhOTU0NGVlYjljYTAyYTE2MWZhOTgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YTcxNTI1NzJlZTg0ZjEwOTA2Yzc0YjlmMmE5MjQzNGRkYWIwZTNjNDI4MTNiMDRlNTk3ZWZjZWM5NTFiMjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjM4ZDZkNjIxNjkzY2EwNzU0MmQxYmJmZmYyMDlmMjI3Y2ZiNDE0ZDJlMTRjYTBjNGRkNjhmNDJmNDdiYjRhMTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQzMzEwOTVkNWZkOTQ5N2Y0M2RkNjkwMWE5M2Y3NDNiYjY1NTUwNmEwNTBlMTVjMjgxNWJmMWM5ZDRmMTMyOTgifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/structured_output.py#L35-L75", + "version": "repo-lines-v1:sha256:a2bf4a871ace64be067a760bc8e146b3d70258e22e7e520cda7efd6ffad87876:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcxMDMxYTdmMWVlMzQwZjU3N2FmMGJiOGU5NjBmNmFiODFhNGQyYmI0ZWViZmM1Mzg3MmNmNDNmYjFhNzliNjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNhYWJhYzAxYTBiNDQwZDU2NzA4Y2RkOTBiOTljYzg4OGNmZTgyNWQ1ZTQ4M2Q2ZDQ2OWUyZDU2MmFkOWViZTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY5NTMxNDczZjMyM2I3NjQ2ZDU3MTIzYTZhMGViZTIwMTFmZjc2MTMzNmMwZjFkN2U3OWYxNWUwMDdjZDc2YzMifQ" + } + ] + }, + { + "id": "claim_27ff2db347af43bab1b671b2b8c9aeae", + "statement": "When ToolStrategy validation fails and handle_errors is enabled, _handle_structured_output_error() determines retry and generates error message; error message wrapped in ToolMessage is appended to conversation, allowing model to correct output on next turn.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1277-L1294", + "version": "repo-lines-v1:sha256:efddcbf95e4f6ce70a8cf505c15f90b7ad95673dd3d6ec4f6083cad1406c8fd0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVhMmE3NTE0MjYxMzE3YzRhOTI2ZDFiYWIxMGZjOTAwNWE3NDZmYmQ0ODk5MTgwNmQ4ODk4NTQ5NWQ1NzU4NjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkYjMzOWMxN2Q0Y2M1NGU3MGM0ZmNmZWNiNjExYmI5ZTdkMzYzZjI1YjE4ZTA5MjNhMWQzMDZjYzk1OTI0ZTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQwOGY3OWZlNTIwMjJhY2E3ZDgzNGM2ZTBjNmEzMjU4M2ZlMGFkZDc1OWI3ODVjZDc4NTY3NThjMGExOWEzZjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFmZWQ2MTVlZmExMWYwYTYyZTBiZmQ2MDZhZDE1NmM3NWU2NTYxNTBlMWYyZmNlODkwZjgxYjVlMjk1YWY5ODUifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L628-L656", + "version": "repo-lines-v1:sha256:00fd5f0b9df0762ae54ede2d60f5bbeb785b3055f04eeebf87c015e0171088de:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZmM2JjYWUzMzhlZjQxNGQ2NmFhM2Q4MTUxNjA5MmE2MWMxNGZhNTllZGExMTMxMmVlMzgwM2UwNDNjZDAyMzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA5ZjViNTk4NTM5MWU3MTAyMWY1ZDZiYjczZTE1YzgzZDYwNmJmNTNhMTVhYzM1NGRhMGI0OWM3YzZjYWZlM2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM5OWMwMTM5N2U5ODE1NGMzY2ZmMDZlZWY2MzhmNjAwODc0NjA1NTA0OTUzNTJlMmQwMDFkMTU4OThmYmUxODEifQ" + } + ] + }, + { + "id": "claim_9b70b8b8ed2b46f9bb445b9a2a64238a", + "statement": "create_agent() wraps raw schemas in AutoStrategy, pre-builds OutputToolBinding instances for ToolStrategy upfront, normalizes response_format at model invocation time in _get_bound_model(), and dispatches response parsing in _handle_model_output() based on effective strategy.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1010-L1036", + "version": "repo-lines-v1:sha256:46ca7abe64408d9241323b742fd554652a4ee0181fe59e35bf62c09fb5599f2d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ2ZTA0NTJmNzY0NWFhYjhiN2UxZDg5NTk5MTVmOTc4NDRkODIwOWVmOTJiZDVmZjQwNDE1Y2QwNjc4ODBmNWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyMjZhNDMzM2Y3ZmRmODhhYmNiOTQ4Yzk2YmY4Mjc5ZWQ2NzE4ZDYxNTVlZjFmZDRiMzQ2MjdjNzMyYTEwZGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIyNjM3MDgzNzA0N2VkYzNmOTZlNTY1MjI5NjZiZWFhYTExYjM5YTEyYTg0ZWZmZWZjYWNhMWFmN2ZmNWQ1ODIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUwZDdkNjYyMDI4NTk1ODI5Y2RiM2U4OWZlZjI5ZDczMjYxY2ZkNTVlZmUxNTdjNDJmZmMxNWQ1ZTA2ZjQ0OWEifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1194-L1296", + "version": "repo-lines-v1:sha256:42ca8ea665535b713f1ff2651387e04941608ec55a949c4f1cf00b8febc38c66:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyYmRmNGQzMWNjY2QxYzBjYmY1ZWRkZjNlZWMxNDZlNDUwYjExMTdhNjJmZGExZDlhMzBkYTBjMzI2YTI2YTJiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyYzkyOTk3NTc2NmYwNzM5NTNlNTA5YzIyYjI0ODBiNjE0NjQzN2FhZmIzMmYyOWJkNTJjMGM4M2E1Y2ViZjU5IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmZjc4ZDc2OWUzNzBlMjA1NmU3MmI2YTU1OTkzMWIzYzVhODNjMGE2YWI2YWFmYjhmZTkzOGE3NTY3Y2Y4ZDNkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5ZjgxNDM1OTIzMTAwNThkODUzZTY3NzM5YTExNzY2NTkyNjdhODM1MTIxYTc0OWI5Nzk1ODU2M2UyZWU3MmVlIn0" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1298-L1440", + "version": "repo-lines-v1:sha256:a9262c2fd1ed4c970da50f7898b4bb2a19482839aef74c97348f9f14383141b6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI4ZjU5YzFiMTk4OWE4MWFiOGFlOTg1NzI5MDJmYTdlOTE3NjcyM2Q3ZDJmNzMzM2I4YzUzZWRhOTE5YWQzMDIxIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxZmVkNjE1ZWZhMTFmMGE2MmUwYmZkNjA2YWQxNTZjNzVlNjU2MTUwZTFmMmZjZTg5MGY4MWI1ZTI5NWFmOTg1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI4ZWJhMTg0ZDc1NzZmY2VlMDFhM2JkYjVlY2QyOTdiMWI0MTgxMWFlMWMyNGJhMThiM2YxNGJjNjRhNWQwNjNmIn0" + } + ] + }, + { + "id": "claim_0fa32e0b43b74b6ea027931573865fc5", + "statement": "Structured response is stored in agent state's structured_response field alongside messages; field is cleared on error retry to avoid stale checkpointed values; raw schemas are passed to middleware via ModelRequest.response_format.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1219-L1276", + "version": "repo-lines-v1:sha256:2e1c8d13f173b0360296f6ff989dfdc681e2c292a53ec116d98d9845e6b2b630:eyJzZWxlY3RlZExpbmVDb3VudCI6NTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI3ZjcwYWU2MjNjMmI5NWNjYzNjNTcxYmY4ZWUxYmE1ZWM5NmNmZjQzOTk2OTk2NGRmOGM4MjMwNTY1NzU5ZDkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkYjMzOWMxN2Q0Y2M1NGU3MGM0ZmNmZWNiNjExYmI5ZTdkMzYzZjI1YjE4ZTA5MjNhMWQzMDZjYzk1OTI0ZTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjIxMzdjYjlhZDVlNGY3Y2FhZGFkMjU1MTVhZmEwY2QxMWVjYmVhMGZjMmI1YTZjZTE1ODc0M2I4NWFiYzM1Y2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxNzVlNWQ2YTFlYjM2OTBmYzQ5NTg1MTZmODlmMjJjYTIxNWZlNGEzZjgyYjg2YWZhNTVhNzRiZjE1YWQ0M2IifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L226-L242", + "version": "repo-lines-v1:sha256:606c549102e85beab88d502650be6ab116496d3959d6cff46a7d1b0520fdc7d3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY1ZmNmMDEwNDNmMTVhNGRhZGJkNjI5ODdiOWM3OWI4ODg1MjQzNDI2ZWY5ZDBiMzNjMzk0OWVlMzRlYzAyYzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQwMDVmOGU1OGIxNGViNTlkNzMxNTJhNGM1MGI1ZTg2OTBmZmNjNDcxOGQxYmQzZWUyYTU0Y2ZhYjhkNzc1MDIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjYzNjg0OWY2ZTA0YTdiZGI3NTc4MTM2YTJkOWU0N2E2YjBiZjMxYmMxNTczMGFhODM4NmJjOWExNDIwZWFiYjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg5YTdjY2ZmMjEyMmJiZmMxZDEwOTZjZTZkODFlMDY4NDY0NjM0ZDA0MjA2OTBlYzVhZTE5YTFmMGYxOWNkMGIifQ" + } + ] + }, + { + "id": "claim_370cae046f494a418956d1a5e1bd6e2c", + "statement": "For ToolStrategy, model is bound with tool_choice='any' to force structured output tool selection; for ProviderStrategy, response_format kwargs are passed via model.bind(); narrowed tool lists ensure middleware-restricted schemas limit model's tool choices.", + "evidence": [ + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1372-L1430", + "version": "repo-lines-v1:sha256:aa1b0852667814ce2bfcfc79603aa740f7e057317ce6a299249e22f4563fb099:eyJzZWxlY3RlZExpbmVDb3VudCI6NTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFjYWIwZmMxZjgzMDgyN2VmMGY5ODM4MDczZDYxODM2OTg4NjgwZjA5MjI2YWMzMzk5ZDAwYjg5MmZhOTlhYzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAwYzQ4NWVlOGQ0ZTMwMTY0MDZkYzIyYjY4NmQwNGQ1ZDRmZjVjY2QzYjk2MWY4OTM3YTFhZjdiYjU5NDVhOGYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQyYmNjYmEyMzkzNDhmYzJiNzY1ZWZmOTRlOWJlODllN2Y5NGU1YmRmYzA0MjliMGUwYWE5MWZhOWY2YTgwYWUifQ" + }, + { + "resource": "repo://libs/langchain_v1/langchain/agents/factory.py#L1391-L1404", + "version": "repo-lines-v1:sha256:5dc5210e993db7baf4a03bcd9eba1c00e7bcba0fbac3983f27c5f7c9babe2e5f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzNzQyYmE5NDI5N2Y0NGZkMzU3YTlkYWU2NzllZmVjY2NiNzE4NGNhOWY2ZjExMTFjMjdhMjM0OTcyYzZmZDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MDMyZTk2NGQwOTMyMzUxZTVkMTg1MjY5MjgxYjE3MWEzYTA5YjA4YjI4NmU2NjFlYTQ3MDMyM2M4MmViNGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyODE2NDcyYWRkNWM2NWU2NTg0ZTk3MmJjMThmZTBiZDc0MTVmYzMxMjBmZTFhYjY4NzY4ZTY3MzNkNWZjYjIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBiZWUyODBhNWU4M2I3NDk5YTE3YmY1MjAwYjAyMTc5YWQzOTNiMWRjZDBlNTY5M2UxNjIwZGQ1OTMyNTMyYjAifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/tools.json b/openwiki/.claims/tools.json new file mode 100644 index 0000000000..1812f51f15 --- /dev/null +++ b/openwiki/.claims/tools.json @@ -0,0 +1,220 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:f95633f03549fc6949b4ba8fc5d1bd13a03843a429ff10fa3284ae68f6ffce88", + "claims": [ + { + "id": "claim_72f3312a1c4a494889542dc6efdbf940", + "statement": "BaseTool is the abstract base class extending RunnableSerializable that defines the contract for all LangChain tools, requiring name (identifier), description (guide text), and args_schema (Pydantic model or JSON schema) properties.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L433-L495", + "version": "repo-lines-v1:sha256:6a47adae8e041510db620fcb71250bdfd6647c85be06727af2fd537f839e5b3b:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3MTBmYWEyZjY0YjkzNmI1MWYyM2UyNTY5ZjBmMzhlY2Q4MmJiYzk2MjcyN2ZiNDJlMTIyYzdjOGYzYzVmZGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzNTY3NDhiNzQ0ZTBkNjZkYmFlZWUzYzcxMGIyZTRiNmQ0MjE0YjgyMzYxYmIzMjhlYWMwZWU1NjNlNGE1NTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdjOTI4ZmFlYWExYzFmOGNkMmY4NDhlYzBhZTVkYmUxNzFkYTM2NmJiNzIyNWIxMzNkMmZmNmFiZmJmMDFlODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhOTNiOTU4MDkyY2U5YWMxYTlhZjVhY2NlYjJmNjI2ZjYxZTZhZGIzYzRjZmRmMTRlZTc0ZjdlYmM5OGVkOGQifQ" + } + ] + }, + { + "id": "claim_2d9e758d50e5458eacd5540485f12310", + "statement": "Tool input validation is built on Pydantic models generated from function signatures via create_schema_from_function(), with schema sources prioritized as: explicit args_schema → JSON dict → inferred from function signature, and injected arguments (InjectedToolArg, InjectedToolCallId, ToolRuntime) are automatically excluded from model-facing schemas but re-injected at runtime.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1726-L1760", + "version": "repo-lines-v1:sha256:823014167156a8554d476abe099af6b783f93971ffa0f435e3de21f90885cbc2:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4OGViMDE3YzI5NzljOWExODAyYTE2N2M2YWQ1OWQ1ZWFhNDRhYWJmZjJkNDM0OWYyMWE1OTlkYjdmNjI4ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIwN2RmZWVmMWE3MDFjNGJhYmE3OTlhNDMyOTZlNzkwN2U1YTU5YTgyZmY3NDNhOGJmODYzZmM1ZGIyNGI0MzkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MDE0OWUwNjlhZDY3ZWNjZjQ3MzY0NmZmMWM3NTAwNTBkMzI5ZDExMDAxOGRjNTc2Njg3OGVhZmExZDYwN2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgyY2UzYzQyZGU5ZTI1NjA5NGZhMGJmZTkzZDM2ZDE0ZDkwNTY5YjRmMWM3ZWZhY2EzMmI5NmE4ZGNkYTliZmUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L263-L369", + "version": "repo-lines-v1:sha256:e15509cce8b2176c0faabcf43a600108637366d128d5c2a9a65008c7b9a79d29:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkNzFjZmJkMTEyZDAwODM3MDA5MjczMmMwYWI0YWFjODMyNzE5NzAzZmI3NWU0MzM3YTIwYThiODkyOWY0NGY5IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmMWY4NzEyZTdmNzg3OTdiMzI5NGQ3OTE3Mzc5NTU1ODcxMWZhNzcwZTI1ODAyNTgxNDBmNDQ3Nzk5MDdmMjNjIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJhNDU0NWE2MTdhNDQ5ODU1ZTc1MjM4ZGFiMmIzYWFjNTNmYjM3YjNmZjg1NzNiYTkxMGE3MmYzNWY4YmY5NzQ3In0" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L724-L737", + "version": "repo-lines-v1:sha256:fe0c1bf6b914b7bf238838b970174e3576447bfe781b5439f7f698f961c33e84:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0ZTI3MjcxZWE3MTgyNjJlNTI4YTY5M2I3NTQ1YmUzYmM1Y2ZhNTdhYTM1NjQ0NWM4M2I4MWQxZDI4MWUzYWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk2YTdlMjZjNDc2MmQ1YjdkY2Q5MGM1NGFlY2FkNTVmZTAyN2FlMWZhZTMxOGEyMWFmNmQ3OTFiNjU2Mzk3MzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE4NzY2ODFlNmNhZTQ1N2M1ODAzODI4MDg0NzZmOTE5ZDdiOTcwOTdlMGI4MmIyZmE3MWRhMzU5ZmE0M2NhZWYifQ" + } + ] + }, + { + "id": "claim_ad9069aed1ea4728af066d6be0a0a52a", + "statement": "Tool schema memoization via tool_call_schema property caches the subset model class and patches the model's model_json_schema() method to cache the generated dict, preventing expensive regeneration on every agent loop and improving agent performance.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L404-L431", + "version": "repo-lines-v1:sha256:dbbe4de33e8b6527b45c6ba743210d4c9c5452cb78d55bbac11c98d9da00091b:eyJzZWxlY3RlZExpbmVDb3VudCI6MjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyNmIxMzQyNzVlNmVhOGU0MGQzN2I5NzM1OWVjYTQ2NTk1ZjE4MWQzMjU4ODY4ZDcyMThlNGRmMzk0MWVmNzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI5ODEzZDNlMGNhZThhN2M0YjQ4YTYzOTdiNTJhMzY0ZWI1MDgxNWRmZWYzZTJlMzFkNTczYTgxZTg2ZGVmZGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA1NTU5ODc3M2Y5MTE3Njk3ODZkMDdmMzE0NTBjNzgxYzY2OTcyMWE0NDAxMDBkNjYxODgxNzM4MzlmOTcyNGUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L676-L722", + "version": "repo-lines-v1:sha256:508d434a0ae5f93490a1fd7e268ff0c3d92480a8561009a5dd468b1b3765600f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZDAwY2YyNzA5NjRkNDRhMTNkZmFjODc1MmIwOWZjYzQzNWIwZjE5YjMxNmFiY2Y4MWE3YWE2NGJiMTIyZDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE1MTBmZGIzZGRjYzU0YmNmYzc0YjNlMTIxZDQyMGEyMGFiYmUyNGE0OTNiNjA3MDVmYTYwNzcyNDBiZjg1YTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYwNjU0NmY4NzU5MGM3MTJlOTZhMjgwYTYxZDY3ZGYwMDYzN2UxYjczNjRlODc0YjNiMGVhYzU1ZWIzZTU4MTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZjOGRkZWUyNmEwYWQyZTA4OTViYzg3YTYzOTU0MzdmNDFmYzk1Y2I3Yjk1YzAwZjRkZjBkNzRkZGRmOWZiZWEifQ" + } + ] + }, + { + "id": "claim_8806cb4b8ed945a6b6b1d0653a5723dc", + "statement": "ToolCall is a TypedDict representing a model's request to invoke a tool with name, args dict, and unique id for pairing with ToolMessage responses; ToolMessage wraps tool results with content, artifact (optional unshown data), status (success/error), and tool_call_id for matching.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/messages/tool.py#L206-L240", + "version": "repo-lines-v1:sha256:f0854aef615a3c71a93da78f9098284d6389c51f6ac1ab01b81ecb05e73dede4:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUxYTA2ZGY5ZTEwNDAwNTgzNDAxZDExNGFiNzE0MTEyYWY0MzhjYjJhMzg4YTQ2MmQ3YTA4Y2VhNWJmZTA4Y2QiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgwMDQzNjdjZWJlMDRkMzQxNDNmZGM1OGY0OGU1NWQ3MWM3N2ZiZTU1OWRjNjJiZTE0N2FjODA1MzY5NmRkMGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA5NjgyYjYwNDhhY2ExYjdiNjgyNzc5ZjI3Njc1ZmFmYjNkNzBlYjhkYzkyOTgxNDdiNmZhOWM2MWM3NmFiMWUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/messages/tool.py#L26-L89", + "version": "repo-lines-v1:sha256:655ae9d5e895e8ee0ac05a0ba2a71b6bac24bd58abe0c387dde0cdf3d256464a:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVhYWYzYjdkMDU2Njk3ZDEyZDRhYWZiNzU5ODY1M2IwZTViNThiNDgxMGZiMGViNTMxMDc1N2VjMDZlNzQwNDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAyYjI1NzhkODVmMDU0N2ZhOTE3ZGE3YjE4ZDIwNzg5OTE4MjlhNTM4MmM3OTEyOGJlMTZmNmYxMDg4ZjdhYjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY0MGFkZjY0MWFjNjE0MTAwZmE2MWFiYmQzYjAzYjFkNjRlNDFiNDMyMmI2NzVjYTI2NGU5OWMzMjg3MTczYjMifQ" + } + ] + }, + { + "id": "claim_e90f0f84740d4057b2778135e1de980b", + "statement": "Tool execution via run() and arun() follows a lifecycle of configuration merging, input parsing with _parse_input() and _to_args_and_kwargs(), injected argument re-injection, function invocation within callback context, ToolMessage formatting if tool_call_id provided, and error handling per handle_tool_error and handle_validation_error configuration.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1009-L1135", + "version": "repo-lines-v1:sha256:059b4e3df79409c57dad310dd370fef936b4aa587fb63db8ad5cb88235049b94:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwMDhjZDk3OTY5Yjk0ZjdjYWJkOWFiODUyY2RlY2RjOWNlOTllNTJkNzJlYzFjZDE4YjhhZWNhMjlhOWUyYmUyIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI4MjBlN2JiNmFkMzZjNjE5YWIxZjJiM2MyYjQzNTVjNjFlNWI1NTc3MmM4MmU2NGU1YmVkZmZkZDQ5YzQzNzMxIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0ZDg5MjM5YTEwYTAwMTRmMjM0ZTE3MjM3NWQxMWZkZWFmNjdjNWFhNzJjZjFkYTMzMjMxMWQ3NGJiNjEwMTBhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJjMTIxMzUwY2E0NmI3Yjc0OWVkZTdlYTBhNmFhYmQ1ZGFjYWNmYmJkMTdmOGRkZTZjMjZmMmRkN2UyN2IxYjkxIn0" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1137-L1267", + "version": "repo-lines-v1:sha256:854816111f0fb475b9f84e2bc52f8f88e9c26c40b84501fd02508b5f90ce9290:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5ODg4Y2RmM2VjMDJlMzlhNjkwZWFiZTY2MzYzMjE2Yzc5MWZiYWU4Yzc5NjllM2VjYmY3ZGVmN2YyY2Y2NzYzIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0ZWVmNTIxMTkxYWNjMzY2NDcyMzI3MDk0NjZiOGY0ZDhhYjk3YTgwZjI5NDE2Y2UxYjY5MmYxNTQ0OGRiZmY3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1OTczOTkzMWU5NWZjNTZmNDE2MWE3YjI4ZjYxZTQ1ODE4MGQ1OWNjNWNjNWIwYzcyNDczOTdiMWRkYzM4MDczIn0" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L778-L906", + "version": "repo-lines-v1:sha256:04f6f62a960788f753ff83bee3995df9648ff89bc098bfb6acac65b701821bec:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0NmQ0ZjFiZmY3NDNjZjQzOTA5MTIyYzM2NGE0OGFkMGNmNjliMjQwZDI1ZDI4NjMxY2JkZDg5YzhiNjFkZTRmIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiODEzMTQ0MmE3ZTU3MDgyZGFjN2MyZDZkNjliM2M1OGFkYTY1MWQ2NGQzNzNkZjRhMjU3NjliYWQ2NWEyNjA4IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIyMWQzMDhjYWMzMDI2YThlMjgwYjhhNmRhN2EyY2Y3YTQ3OGQzYTg5OGI5ZGEzNDE4MWZlOTIyNTZiY2ZlMjIwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJjN2YzMzFhYzgwNDBmMjU3MjQ0YjZmNDRlNjYyNmVhYmU0ZDk0ZTVjZjcyZTNmMjI5MDRkN2I2MDQxZjJjYmIzIn0" + } + ] + }, + { + "id": "claim_7707ccd23f1f4847a370d637edf131aa", + "statement": "Tool and StructuredTool are concrete implementations with different input handling: Tool expects single string/dict input and defaults to string schema, while StructuredTool supports multi-argument tools with schema-driven parsing and both sync func and async coroutine execution.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/simple.py#L31-L98", + "version": "repo-lines-v1:sha256:60965c519a1861af668a2231710a9d6d6eaf4a618d0e49d766679f87ee91d776:eyJzZWxlY3RlZExpbmVDb3VudCI6NjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUxNDRiNjkyNjE2MDM2NmQzYWVmZTE3ZWJiMDgzZGEwMzk0OWJkYzUxMzQ5NmZmMGFmMjdlNTMxMDg3NmQwZDkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRiYWZlOWEwOTczZDA3YjRhMjM0ZjVlNjg5MWVmYzZmMzRlNjM4N2ExMWUwMTAzZmZjMWZlZmMxM2FjMWQzM2UiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRlYzVmOWFhZWZjNGJhY2YxZGM0ZDEwZTMyOGZmNWE2NWFjNzA1MmJlZjI1NzNhNWFlZWU4Yjg5ZjJmM2EwN2UifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/structured.py#L93-L186", + "version": "repo-lines-v1:sha256:e6e8e5ab99b794875b0596a1326b0f368375525e5ec9b29633f590cb20ea1744:eyJzZWxlY3RlZExpbmVDb3VudCI6OTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYwNGEyZmU0N2EzYTE0MzZkZGMwMWQ0ZTE3YWU4ZjhhZjBjYjNjYTY2ZTQ1NmU4N2VkODQ1ZjU4YjYwYTA2ZjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU0YzE2NjhlYzljZjAyZjcxYjY5YjUyMzg5YTUzYjNiZjU2ZDNiOWJlYmUxNWNlM2UxMGJiZDczNWY1ZWFjNjEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZiYzU3OWMxM2QzZTY4YzQ0ODUyOGZlYWMyYWUyNjE0OTI0OWQ0NDdkY2I3Y2VmMjU4ZmU4Mjk2YzgxOTUxMjUifQ" + } + ] + }, + { + "id": "claim_317bb1db0033404ab1914ce2007861dc", + "statement": "The @tool decorator provides the primary user API for converting functions into tools, supporting multiple forms (no-arg, with parameters, with name, with Runnable) and automatically generating schema from type hints, with description precedence: explicit param → function docstring → args_schema description.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/convert.py#L270-L349", + "version": "repo-lines-v1:sha256:05edba08624bc79018392011be7d7118ecd46060ba612bdddc258e03872cd9ec:eyJzZWxlY3RlZExpbmVDb3VudCI6ODAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJmZDdjZTI2Y2RlMmZkNGJmMWEyODkxOTM2MTQ2MTFkZGFhNDZiNTBjNTY0OTQ5YmJhZTA2MzViYjVlYTY5ZTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImViMjBlNjllY2NiODRiZjZmMmFlODJmOTUzNjViMzQxNDVkZWIwNjcxN2VkYzBkMmUwNTY1MWU2OTM1YTgyNWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY4NzkxODRmYTVkYzhiZTQwNzAxNjcxZWExNWI2ZTY3MzIzZGI5OWJiMDAwYzA0MDM3YWM2YTNjOGQ5NjEyOTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk3MjJhMTk0MGU4YjMyYzU3NDlkYjU3NWM4NzE4ZGI2ZDM3M2IyYjc3ZTExYjg2YjE0ZjlkYmRkMjJiNzBjZGMifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/convert.py#L77-L349", + "version": "repo-lines-v1:sha256:71c9f4464fa42f4843a5e7a5ee10eaae3d1729e874675cc53a22794b680a763f:eyJzZWxlY3RlZExpbmVDb3VudCI6MjczLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI4ODk0OWJmOGVmNDJkNDNkYzA0NTVlODY0MTYxMTk3MDJjMzQ4ZTRmNzhhNTgzMzZlYWMwMGMzZGU0MzJkMWU4IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlYjIwZTY5ZWNjYjg0YmY2ZjJhZTgyZjk1MzY1YjM0MTQ1ZGViMDY3MTdlZGMwZDJlMDU2NTFlNjkzNWE4MjVjIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5OWFjZDVlMjA3NGJlNjU5OTBjN2YzNWVhZTdiMDU5OTAyYTUyNDZjM2NjNTBjMDNkNTMxYTMzN2Q1YzJiMTY3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5NzIyYTE5NDBlOGIzMmM1NzQ5ZGI1NzVjODcxOGRiNmQzNzNiMmI3N2UxMWI4NmIxNGY5ZGJkZDIyYjcwY2RjIn0" + } + ] + }, + { + "id": "claim_b8b7ce905c2b4829b6e04c6f7e9907cc", + "statement": "When parsing docstrings with parse_docstring=True, the tool decorator extracts Google-style Args sections for parameter descriptions and validates that documented args match the function signature, raising ValueError if invalid or mismatch occurs.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L126-L201", + "version": "repo-lines-v1:sha256:1408a7fb98ec2dc25bb6972030249b843d79eb03f8394ab563da0c931efc2e96:eyJzZWxlY3RlZExpbmVDb3VudCI6NzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhlOWYxMTM5NmM2YTk2ODUxYmJiZDYwNDMxMDA3MTRmNDBiNjA1ZjBmZDk2YzE0NmZhNjFlNWE3NjYzNjQ5ODciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjY2QxNTRkN2UzMGM3ZWM0YmU3ZGQ4OTY4YTEyYzZiZGEzMDY2NGI2OGJhNDYzYmMzYzFjNWQ0MDQ0MzIxYzciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ3MDE0OWUwNjlhZDY3ZWNjZjQ3MzY0NmZmMWM3NTAwNTBkMzI5ZDExMDAxOGRjNTc2Njg3OGVhZmExZDYwN2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAxN2RkMmIwYTYwMzA0OTk1NWE3MzU1NTI0Mjk0OWJlYTQxMjYxOGQxMTdiNjMwNmQ2MDJlNWVhOGFiOWJhZGYifQ" + } + ] + }, + { + "id": "claim_baed17b58af94cbbaea5fb30dae1ee5e", + "statement": "ToolException is a custom exception for controlled tool errors, and tools support flexible error handling strategies via handle_tool_error (bool/str/callable) and handle_validation_error (bool/str/callable) configurations that catch exceptions and optionally return error messages or invoke custom handlers instead of raising.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1115-L1126", + "version": "repo-lines-v1:sha256:8eaf8e3f2c68fae527c1405b883fba67653bf1cba8aeaadb17ce4a4b14602d01:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRkMDJlMjdlNWZmZTZjYzA1YTc3YjQyM2VlZjVmZjg5M2YyMzA5NmUzY2FkMWMxZjdmODk5NzE1OGQyMzcyMGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRlY2ZkNzYyYWIzNDY3Y2JiNWJjMzRkODZiMTJkZGQ1NTAzYWM4NWFhYjk3NWM1MzY1NTg3NDllYWI1NDJjZTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5ZTlkMDExOTY1ZGRiMjI1Y2YwNDNlNDY2NTRlZDhjOTBiNGZlNDEwMmM3MjAwMWMzZTMwOTc4ZWNkYzc1ZjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE0NDlhMDhhOGQwMGQyMjRhYzAwM2NiYTAzZjk0YmRlZGFjYzgyMGU3M2YzNzMwMzZmM2Y0MGIwNzViZmM0NjUifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1281-L1349", + "version": "repo-lines-v1:sha256:e5a5fbc1d9b22b6b452e5775f901887b32f6f3f56707481ca49005609195f360:eyJzZWxlY3RlZExpbmVDb3VudCI6NjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIyNDFhYjE1ZmMzMmJmMmRmMjdjZWM4OTI0NmI4OWI5OWJmNGRjMTBkMmJiNDk5MDgzN2FkMjQyYWY3N2NmNzgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRhOGE1ZTU5NzYzOGZiYzZlMGZkMDJlYTZhMTA1OWU4ZGVjNWJiY2FhNzI2ZDJiMzllM2UzMjg2YTcxN2EyYTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY2N2RjZTM1ZWE1YThjYWZkOGEzMDRlODg5YzRmYjIwYjUyOGE5YzFkNzBkNzgxZTA5ZTJjNThiYTk3Y2RlN2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3ODdkODljNjMwMjhmM2U0ZDEwMDAyM2M1YTU5ODRiMWYzMDQ2Njg2Mjg1Y2IxMTQxZjdkMWFmM2ViNjM2ZTQifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L371-L379", + "version": "repo-lines-v1:sha256:6754e030f2664cf2bb941a8212da9d42ca14e83d38ea428a826bd04c72994e7d:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODRiODgyODBlNGU2Njc2MzMwZDYxMDFhNmZkZGU0NzE4MzE3ZjQ2YmFjOTVjMDU5OTIxY2UzMzU2Zjk2OTczNiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOGNjNGE3ZDFjYzc5NmFiYTNlNDY3MjMzMjdmNTE3ZTlmODExOTgyZDI1MGYwNzAyOTg2ZTRiNTc2NGFkOTU4OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDc2NzJlYWQ4MzMxMDQwYjQyNGM0OTc5ZWQ1NDA5MGIwYzJiN2VmZDIxNDBmODU4OTQ3N2RhM2RiNTc2NjU1MiJ9" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L527-L545", + "version": "repo-lines-v1:sha256:070d21a32867a76d53cbe6a1da7dedd3aa94d9a418f3bb4c1ea820a451d66a34:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY5NDMxZTg5NTMxNTY0ZTE5NjhiZWNmNzk3YjYyZmUwNjE2MzQ3NjA5NDE0M2M4NzQyOTNmYzFhMzg4NzliYWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUzMWY4NTVlMTY1MmVlYTk2YWZhM2M3Y2JlNzUwYzhkMzNmMzgzMzc3YTFjMzE4ZTI4ZTRlNjFkMWY1NTMyYWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI2MGU0NWIxNjFlOTAwYmY3MjY0NjE0NjY4MzljMWZhMTBhOTRmMzU5NzFmODA2MDRiNTNlNmFmNDE3ZGVjZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUyYTVmOTMyYzFiYzVjNThkMmIyOGM4NWM4OThmMWY0NjlmNzUxNDdhYjU2NTVjYjNlZDlkNDQ0MDFhZjUwOTQifQ" + } + ] + }, + { + "id": "claim_3a191aca2c724f1bad62facf513476c6", + "statement": "Runnables are converted to tools via tool() decorator or convert_runnable_to_tool() function, which extracts input_schema from Runnable.get_input_jsonschema(), validates schema is object type, wraps invoke/ainvoke to inject callbacks, and delegates to StructuredTool for multi-arg or Tool for string-input runnables.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/convert.py#L282-L347", + "version": "repo-lines-v1:sha256:be9637728742a7c0d5d7f274d54c4868e644ad3831d95a911a32feb26ddb3487:eyJzZWxlY3RlZExpbmVDb3VudCI6NjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhkZWM4Njc5YTU4ZjgzMzJjYWVlY2VhOGUzOTUyNzA0Yjg5MmQ2MGM5NmFkNDc1NTY3YjcxMTg4NWY0MWNjNjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MDMyZTk2NGQwOTMyMzUxZTVkMTg1MjY5MjgxYjE3MWEzYTA5YjA4YjI4NmU2NjFlYTQ3MDMyM2M4MmViNGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNkZjI2N2I4YjdhMGY3ODIwODg5MDk4YWU3ZGQ3NjI0ODI4YTE3MTVlZTU4NDNiOWRkNjQ0NzBlNzQ5ZWI5ZjEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJjMGQ4ZDA4ZjNiMTk1ZGMyMDFmM2FhNmM2NGZlZDQ5MGYzYTM0OWQzNmZiNjA0NGI4YjI4YmQzYWIxMDE1MTIifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/convert.py#L433-L497", + "version": "repo-lines-v1:sha256:7d4c65262b73848a9f76a454f44d3164aac00fed01d9865313a3b75d1bc12a50:eyJzZWxlY3RlZExpbmVDb3VudCI6NjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFhMjdmOTcwOWIyNTc4N2U4ZDRiNWMzZDA5ODcwODQ5MTVjOWY1NmIyOGEwNmNjY2NmYTgzMzBkNWE2YWQ5MjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwYzFmMTg4ZjE0MmVkN2E4MjQ2ODBiMjA3MTcyYzU1MTliYzYxNGE0OGZkYTdjZjE2N2VkYjhkZGY3OTk1NmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYyMDVmZDRlNzNiMzFkMTgyNzg4Njg3MmY5ZTEyN2NjMTYxY2I5NjNiMDBhZDUyZDc2MjEyMzM1MzlhOTE4MGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_97f0b6e914604669a604feaf14fb63c5", + "statement": "BaseToolkit is an abstract base class for organizing related tools via get_tools() method, enabling logical grouping, conditional tool availability, dynamic generation, and integration with agent initialization pipelines.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1935-L1948", + "version": "repo-lines-v1:sha256:137c80ba35c4a78fe6ffe49d6ce53e983917cfbb0eb8b8b65ec4cab373e2aac8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM3NDFjYzJiZDhmYWYzOWQwNTI4ODI4YjAwMjAzY2EyZDAwNTIxN2JlOTNkNTJkMmJkNjNlOGI0YzY2NjJmYmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUwZGUyNTg3MTk2MTQ5MjYwNzFjOGZiMTlmZDg2NjE1MDU1NWYxYjAxMzU0ZjhjZDE2MDNhMTA5YTdkZGFlNDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFkOWVlNTUzYTQ0ZWNmN2Y1YTY5ZDU5YjliNTgwMDVjMDVmZTU3ZWVlYmIzYTEwYTFkNTUwYTgzNjM3OTExZjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_da7f88346cdd42b68c3f04b55a5aa1d3", + "statement": "Injected arguments via InjectedToolArg, InjectedToolCallId, or ToolRuntime annotations are excluded from tool_call_schema, identified via _get_injected_args_keys_from_signature(), re-injected during _parse_input() from tool metadata or invocation context, and filtered from callback inputs via _filter_injected_args().", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L1617-L1677", + "version": "repo-lines-v1:sha256:dcab87fe71f4a411f9d524cd735c89ab16217f98395dcb732ebf6f12833dda41:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyMTY5NWVjZjVjN2UzY2NjYjcwOTExMzA4ZmUzMjlmNzAyMjBmMDVlMGUxZTA1Zjk4OWU2OGQ0MmY4MWM0ODgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjliOGNiZjRkNWU4MGJiZTVlNDhjNTdmNGZjYjczNTY3M2Q2MDc3NDBiNDJhN2FiNmM5MjM2ZTM3MmE5ZjUzZTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFhNTQ5YzAyZmNlMGQ0OGZiYTdmOWJiOWUwNjBkZWVhM2M2NDk4MjkwYzA3YTc5MjM4ZDZiMDVlNGVjN2RkNjMifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L889-L906", + "version": "repo-lines-v1:sha256:b2e83b0b3bcb95fc5a63d147e373b195ea832440fcb5cecad373b0acb51f88b6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM1OTU0ODBjMDk1NjhmODkyY2JlNzI5ZjdkZjBiNWJlYzMyMjk5YjczYjNjYTQ4ZWQ2MjhmOTExNmY3YjNmZWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI4MTMxNDQyYTdlNTcwODJkYWM3YzJkNmQ2OWIzYzU4YWRhNjUxZDY0ZDM3M2RmNGEyNTc2OWJhZDY1YTI2MDgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA5YWUwMmE1MjBmZjdhYzVhMGZhYWUzM2YwMmZhNmFmMDFlNWZmYTM5Y2VkYWE0YmE4ZDRhNGIzYTQyYzFhNzkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM3ZjMzMWFjODA0MGYyNTcyNDRiNmY0NGU2NjI2ZWFiZTRkOTRlNWNmNzJlM2YyMjkwNGQ3YjYwNDFmMmNiYjMifQ" + }, + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L934-L969", + "version": "repo-lines-v1:sha256:305049d91b4db5ab081bd739dd9ccbaff19928e8b2b5cbea83ab799e52278299:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZmMDc5ZjYxZTBkYmVhYjliNTI1ZmM4ZDEzY2RmZGU1YTQzZjhiM2NjOTg3MmFmNDI5MmY1ZmMzNGUyMmM5YmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI3NDQ3NWZhMzFkNTNkNWIxMmNlZmE0ODE5MmE1OGY1YjA1NGRkOTkzOGI3MjMzYWI5OGU1YzU2ODQ4ZGY3YjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMyYjA5YzMzMTVjMDYyNTZiMjZlZjIyM2E3OTFiYWFmMzYzMjU0NjY3ZDI0MTJmNzU1NWY2ZmJmNGIzYWVkNGYifQ" + } + ] + }, + { + "id": "claim_4ba3489fcb154a0ea0ea519a683d9224", + "statement": "Tool rendering for models via render_text_description() and render_text_description_and_args() produces plain-text or args-inclusive formats for prompts; models receive provider-specific tool schemas (OpenAI function_calling, Anthropic tool_use) generated by function_calling.py utilities converting tool_call_schema to FunctionDescription dicts, ensuring models never see injected arguments or reserved parameter names.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/render.py#L13-L67", + "version": "repo-lines-v1:sha256:c7e5748f94e34c198e082043ab39f9b44529842aafa9ee598a5139ad704c0041:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNjZGI3NGUxYTA1ZTMxZmUzZGJlNjRlNWQ2Njk0YThkYjQzOGI5NGE5ZDJmMDEwYmU1YjMzYmYwNzZkZDI3NTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAzYjA1MDcwMjllZmNkZWJiZDE5ZTUxYWExZDRlMjIzNTBiNDk1NDE1ZWY4NGViMzllNmZiMzk2MTUzZDRiM2UiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ5MzM4ZDQ2NGQ5OWYxN2YzNGM1YWZkZjQ3Nzg0NDkwMWI2ODdiZjY1Nzc2NzE1ODlkMWIxZjNiODRhYzdiZDciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_eec55eaa3bd649d494ca43f5d7ca56fa", + "statement": "Tool schema caching is managed via __setattr__, model_copy(), and __getstate__: memo is cleared when name/description/args_schema reassigned, on deep copy with update touching those fields, and on pickling (dynamic classes cannot pickle by reference); memo is rebuilt lazily on next access.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L637-L674", + "version": "repo-lines-v1:sha256:047e75c162a81b2782109693dbcc1d9b36c1b1800b90e0fe1455a351575acf83:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMxMGExMTJiZWFkY2ExMDM0MjczMTcyMzkyN2NiNDVmMGJkNmRlMmFhZGNkOWMxNjk1NjQyOTIzNWMxNzMxMTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4MWU4YzJiMjIwYWE5NzYxNmE0YTEyMmQ3ZmQyMDkxMjJlODhhNjJkNmQ3MzA4MGM5NTEwZWE4MThhOGJhYjAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZiYTU3MGU4YjQyNTViNDE0MWIyY2Y0NWMwMWViYjdhYWFiNjAwOTE2ODk4MjAxNTI5OTY1MmMwNmFhMmJkOGEifQ" + } + ] + }, + { + "id": "claim_87a5269d264d44b8a35beaaea99fcb66", + "statement": "Reserved parameter names (config, run_manager, callbacks) are filtered from tool schemas because they conflict with LangChain's runtime injection; ToolRuntime annotation provides access to application state, context, and LangGraph store as an alternative.", + "evidence": [ + { + "resource": "repo://libs/core/langchain_core/tools/base.py#L74-L74", + "version": "repo-lines-v1:sha256:d08461449e1929b1c506abce9bf6d9ec63f30eac6f90acf6d8a4a7ee0e03ed50:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDA4NDYxNDQ5ZTE5MjliMWM1MDZhYmNlOWJmNmQ5ZWM2M2YzMGVhYzZmOTBhY2Y2ZDhhNGE3ZWUwZTAzZWQ1MCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZDA4NDYxNDQ5ZTE5MjliMWM1MDZhYmNlOWJmNmQ5ZWM2M2YzMGVhYzZmOTBhY2Y2ZDhhNGE3ZWUwZTAzZWQ1MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMmY3YTQ2OGU3M2NmMTZhMTlkZTEwYWYwY2ZjYjI3MGJjYzViZWZiODg1MjQ5NGQ1Y2U2ZWEyOGMwYzQ5YTk3YiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMTU0MzE5NTk1NTIxM2ZkNjkwODFmODM3ZDQ3MGEyZGQyYzI0Zjk4NWQyYzg2MmZhMmE5ZTNmNDBjMTkzOWZkYiJ9" + }, + { + "resource": "repo://libs/core/langchain_core/tools/convert.py#L104-L113", + "version": "repo-lines-v1:sha256:436274b5dcf2c57abdf86c1e0fc3c0061283c60a27a1158bd1466176b72f82d3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM1MzViY2E3YjMzZGRhMzdiNzljZDFlNGYyNTVkYmY2YmFhMDdjZWVlMGZiYTU1ODMyZTkyYmQ5ODkxODlhY2MiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk1ODg0MGQ5YWJjYmIyMjYwZDE3ZWFiNmFkOWQ3ZDlhNzcwNDc3OWZhNGVhZjA1YjlkYTMwNTFmZjNhYzNhMWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJlODBhOTE5MDFiMzg5ZWIyZDA4ZmFlNmRlYzM2Y2JlZDYzNGEwMDUyYTU1NTNlNDZkNmE4YjYwMTgzNDhjMjkifQ" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.claims/unit-tests.json b/openwiki/.claims/unit-tests.json new file mode 100644 index 0000000000..7697411ef7 --- /dev/null +++ b/openwiki/.claims/unit-tests.json @@ -0,0 +1,194 @@ +{ + "schemaVersion": 1, + "pageVersion": "sha256:7f15b0e6bb2c95e703a2e0c8f6fe04091727b2cb26ae3a3260124cd43f1394e0", + "claims": [ + { + "id": "claim_bdde90f4e5f6413f8214fe7e80b66a9a", + "statement": "Unit tests live in tests/unit_tests/ directories organized by feature mirrors source code structure, integration tests in tests/integration_tests/, and benchmarks in tests/benchmarks/, with each test package running unit tests via 'make test' with --disable-socket and -n auto for parallelization.", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L13-L20", + "version": "repo-lines-v1:sha256:ab8103f9ee7c85d9fb1c9d3d0cbe6264d1384aac292f90c74f84c77c06ed5398:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDliMzBjZTkzZjhkMmVlNmQ2NDdlYjNhN2RhYjQ0NjRiNWQ5Yjc5ZGExOTI4ZGE2MDY4MDQ3M2QyY2Y0ZTY4MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTdmYmQ4MDQxMWFjZWNkZDkxOGI4ODk4NDE3MTg3MGY5NWU4ZjE3YTZiNDA0YmU4MDY0YmE3NmJhNjc2NDk3NyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjQ2M2RlMzg2ZTA3OTFhZGQ1MmUzZjZlM2EwOWYyYmY2NDhmZTQzMTMxOGUxOTE1YjdlNWRlOTlmN2E1ODQ1MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzgzNzQ1ZTU1YTIzYmI0MzZjYTUxMGFiMTgwNGQyODMwMjhhNTJlNzc5MjE5YmNlNzAyODRkOWY3YjEwMDcxMCJ9" + } + ] + }, + { + "id": "claim_20d69ecaf7e34bfd922eda9176b4adba", + "statement": "The conftest.py blockbuster fixture (autouse=True) prevents blocking I/O in async code by registering allowed blocking calls in specific functions like os.stat in langchain_core/_api/internal.py and os.path.abspath in runnables/base.py.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/conftest.py#L12-L32", + "version": "repo-lines-v1:sha256:aeb3791f6f39690343a2afea35c9e4e3af46496f963132474d552d66b44088d7:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmMDg2NmY0MDExZTU4NWZmYzA5MDkyZWRmMDgzMmUxN2I1MmY0YzkwYzRjYTBiNDJkY2ZhMDUwZmEzNzViNWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM0YTIxOTliM2NlOTNkNThiMzk2NmUzMzFjMWFhOTQ4M2U1OGYwODQ4NDJjNzFmNTVkNDNmZTk0ZDAzNTVjMDMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE4MDI2ZjhmYzBhNmUzYzQ1NDk0ZDhmZGI5ODgwNmUwNzNiODI3NjgxYzViMzk4ZjE1ZjI0NWQ2ZmU1MDEyZmMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzZjUyMTNiYWM0NDkxODk2ZmJhZmQ0OTI4ZjM5MWZlMWViMWRiOGNlMWU1NjU4NDdhZDNkOTc1NmQ1ODY5ZGIifQ" + } + ] + }, + { + "id": "claim_0aa8ee2a92104476b6ad8c29cee63cd5", + "statement": "The pytest_collection_modifyitems hook implements @pytest.mark.requires(package_name) to automatically skip tests when optional dependencies are missing, checking with importlib.util.find_spec and supporting --only-extended and --only-core command-line flags.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/conftest.py#L49-L112", + "version": "repo-lines-v1:sha256:95b649751061636bd40423bcb3a5223521d72cd21006b700e58264716a0a8676:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmYzYwZGExMTM1NTc1OTRhYTlhZmUyNjJmYjUxZjNhM2RlMWNhYTdjNDdhNTg4NTExYjdlYjA0MjY3OTY0ZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlZWYyZmFhYjFjY2ZhMmQyZDI0MDZmMmUzMWI3MjM5MGY0MDFlMjFiMTRhNzIwNjUyYzkyM2E2N2I2NTdjNDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhjYzRhN2QxY2M3OTZhYmEzZTQ2NzIzMzI3ZjUxN2U5ZjgxMTk4MmQyNTBmMDcwMjk4NmU0YjU3NjRhZDk1ODkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgxYzE0MjZiZWE3ZmFiYTA1YTEyYzFlMWM4NjBkYTY3YzYxNzM4ZmZjY2NjYTRmNjc2ZTRiOTI2ZGVhNjZlMDIifQ" + } + ] + }, + { + "id": "claim_8c85d4a54ea54ec0ac2244faba08cbb6", + "statement": "The deterministic_uuids fixture replaces uuid.uuid4() with a deterministic generator (00000000-0000-4000-8000-{i:012}) for snapshot testing, enabling stable message and trace comparisons across test runs.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/conftest.py#L115-L120", + "version": "repo-lines-v1:sha256:170766da8ca0c06791c54c297209de43b5b78f554afa3a942c9960fa1c20cd77:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMjNmMzY2YjliZDlhOGQwZjM0MGEyYmMwYmUxZTIyZWVhMjA1NjhkZDc4YTQ1MjgxYjcyMWIzMGYyMmQwODdmMCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZjE1ZDM5NjBjNmY1NDc2MmJkZjI1ZWZkYzY0ZmJhOTUzNTA1MTAwMTQ5MWIyMTNmZDc3OWMyZmFmNTM2MzRhMiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYmU5ZDFlNWRiMjczOTlkM2NkOGMzN2NiMzhiZWFhYmViZDllMjQ0Njg0M2ZlM2Y2NDhlOTZlMjMwYzk2ZmM1YSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" + } + ] + }, + { + "id": "claim_69fa7a0282f74dacb02b906474eb001e", + "statement": "ChatModelUnitTests in langchain_tests.unit_tests tests chat model initialization, sync/async invoke, streaming, tool binding, structured output, serialization, and message handling; implementations override chat_model_class (required) and chat_model_params (required) properties plus feature flags like has_tool_calling, has_structured_output, supports_image_inputs.", + "evidence": [ + { + "resource": "repo://libs/standard-tests/langchain_tests/unit_tests/chat_models.py#L277-L340", + "version": "repo-lines-v1:sha256:1f718c2c9f4f47dbab87594675c8ec1115ece927acebc63636acaec7f640bbfd:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyYjg1ODY3YTRhNWZiNGJlMjFhOGNjMTg3ZjY3NmM3MjQ0OGFlN2EzNTdlMWZlMTlhNGZmYmFmMDQ2YTFlMDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhMjJkZDMxZjcxNWVmZGQ4YjAxZGYyYTY0Mzc3OGI1MDIxN2RhMmE1Nzc0ZTBlYzA2MDNlZjczNDhjYmE1MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjczM2U4NDg3Yzg1ZGZkY2Y5ZmE3YjkzYzc0NmM5NDYyNWFlNDdlMTIwYzc5N2U3OTFlNzM3ZjllZDdmZmM3M2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBmYzRkMTFjMzdiMmJiZTI3MDBjNDk3NGY5NGNhZGY3NGE2YjU1ODZhNjU3Y2ZlZTNjZmI1ZTQwNTlhODEyZGQifQ" + }, + { + "resource": "repo://libs/standard-tests/langchain_tests/unit_tests/chat_models.py#L43-L114", + "version": "repo-lines-v1:sha256:c89ea4f0203e8faedc0a38d530353b239af1c28047c7e25f6afd1d4277c0c67b:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYzMmJhYzM2ZjYyMmI5OGI2NTFhZGRkZGQ1OGMwZjYxY2IwYjNhMzEzMDkxODgxZGMwNTVhNjU3NmZmZDk5MmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJlYzk2NzJmN2E5MTgzNDY3YmY3MGFlOWU2ZWNkOGZlNGI3MzY4OGNhNGRhM2JkYzI3Mzk5OWRlZGUwOTlmY2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFiODI4NzI0ZjFkZDg1M2ZiOWZkZTYwMTAwNDlmODk2ZDczNzYyMmZkZTczOGY3YjE5YzlkMWVhZDFjYmRmNzEifQ" + } + ] + }, + { + "id": "claim_5c28a970c23243bfae5b52f84fff9f40", + "statement": "EmbeddingsUnitTests in langchain_tests.unit_tests tests model initialization, single and batch embedding, and environment variable initialization; implementations override embeddings_class (required), embedding_model_params, and optionally init_from_env_params.", + "evidence": [ + { + "resource": "repo://libs/standard-tests/langchain_tests/unit_tests/embeddings.py#L34-L137", + "version": "repo-lines-v1:sha256:42d46123afc278b109c06bc60c58cdf02b678c9dd74bc43a089144ef1d2d52f4:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwZWQ5MjJkMTJmODRhMDFjMDQyZGI1ODVmMWNjODFlYjdlZjQxNzhlNWVlYjk0MjZmMzM5NmIxYzFhYWQ2NTZkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJmOWEwZmUxMDkyOGQyZTU3OWM0MTI4NzExMTlhODMzYWMwYjc3ZTUzZTg1MWFkMWU5MTkxNDQ4ZGQxMmM1MjFiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlZDFiOTVjNTFiNzg3NjE5NjhjNzMxZTgxYjM0MjJhMjkyNmE1ZWEwNzE3NDRmYjM5MTMyZmIxODQyZjFhODNhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MCwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0" + } + ] + }, + { + "id": "claim_14899da35b584370acfb982299389923", + "statement": "ToolsUnitTests in langchain_tests.unit_tests tests tool initialization, invocation with example parameters, schema generation, and environment variable initialization; implementations override tool_constructor (required), tool_constructor_params, tool_invoke_params_example, and optionally init_from_env_params.", + "evidence": [ + { + "resource": "repo://libs/standard-tests/langchain_tests/unit_tests/tools.py#L58-L99", + "version": "repo-lines-v1:sha256:6717ce0c9225e740ec2b325bfb649d78b3e5249f205d5d18effc28235aadff26:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkMTcxNDY4YzQzNjBkM2NhNzJhODg5ZTIzNzM5Y2NhZTNkYjU1ZDY4ODQ5YTc4NmFmN2ZhYTcyMTZlM2I4NjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5YTBmZTEwOTI4ZDJlNTc5YzQxMjg3MTExOWE4MzNhYzBiNzdlNTNlODUxYWQxZTkxOTE0NDhkZDEyYzUyMWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFhNDNkZDhmMGE4MDFmZGQzYmFmZTE0MzJjZjg4MGJiOWU0ZjVlNmQ3ZTE2NTI0YTZkM2ZiYmUwMGM2OGJlMmUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ0OGNjYTY1YjQxMDE3MWFhNmVkY2E2Mjg0MDZiNGVkYmQxNzVkMDEzNGVjMjkxMDhiNjM5MTNiNjFmY2UwYzYifQ" + } + ] + }, + { + "id": "claim_181512f0db394d50b54bbaca6cbab961", + "statement": "BaseStandardTests enforces that subclasses implement all standard tests without overriding them without @pytest.mark.xfail(reason='...'), preventing accidental test deletion or modification and ensuring consistent test coverage.", + "evidence": [ + { + "resource": "repo://libs/standard-tests/langchain_tests/base.py#L4-L70", + "version": "repo-lines-v1:sha256:1f2cb064b27316502f65d806163631654d1f8627609dafc40f46fa11505c1c51:eyJzZWxlY3RlZExpbmVDb3VudCI6NjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0YzJlNmY3MDI2Yjg0Y2NiY2VhZTBhZTkwZTY5YmY3ZDFlZDc2NDIzZjNkMWJhZTJkOGU3ZDI2YjkwYWU3N2MiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImIwNDlmNjM1NmEwNGI0OTExNDYwMTg0OTIwNGZlOGVlOGU0NGM2ZWIzNWU1YWE3YjI2ODJiNWZhZTNmZTRlZjkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_84ceee7523804c2eaf1897c4f9bebfaa", + "statement": "FakeChatModel classes (FakeListChatModel, GenericFakeChatModel, ParrotFakeChatModel) in langchain_core.language_models cycle through pre-defined message responses for testing without API calls; FakeListChatModel uses string responses, GenericFakeChatModel uses AIMessage objects enabling streaming tests, and ParrotFakeChatModel echoes input.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/fake/test_fake_chat_model.py#L27-L49", + "version": "repo-lines-v1:sha256:d5f4e4912eaa2d57220322a7a05b20b26d9c9d44ebcecd81480292ad59fda079:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3OTI5ZDk3NjYyODhhNzNhMGJjYzFlOGJmM2FjYjJiMDRhMDI5Njg5NDI3N2JmNTFiYzg4MTRiZmMyOThiZGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5OGIwNjNkOWI0N2U2NTM3OTYyNTU3MmNjZTE5NzY2OWQ1NzY3ZTEyYzE4YmUwNzU2NTJiOGNkOTgxY2U4ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI3ZDA4NTQ1NGRmMjUyODkyNTY2NmVhYTY1MmQwMGFjNmQzODNmY2JkZTlkNjllMzU5ZThjMjg1NWE2OGJhNzYifQ" + } + ] + }, + { + "id": "claim_78a2ce40e5f148b99a301a3ed6098017", + "statement": "FakeCallbackHandler in tests.unit_tests.fake.callbacks counts callback events (llm_starts, chain_starts, tool_starts, etc.) with granular per-component counters and total start/end/error counts, enabling assertions on callback invocation patterns.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/fake/callbacks.py#L119-L150", + "version": "repo-lines-v1:sha256:9586cf86900e318ac9f00979847d7020d99d637acdb178fe1decb4ac79ba71a3:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY2MGQ4YzU0OGUyMzNhNDNhODY3ZGIxYjA0YTdmYTNhN2JkMzkwMzk5MjgzNzQ2ODYwNzI1N2NkOTkyMzQ3OGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0ZGE5MzUwMDE5ZGMxZTY2NzQ1MjY0Mzg3MzE2MTk5NzIyNWU5Y2U2MDEwN2FmN2I2NGY4NDBkZGVjZmRhNGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImExYzkwZGFhMGJmOGUyNDMyNmM0YWRmNTJkZDRmODVkNzFjYTFiODZkOWY0MGUwZjBhOWYwNjU0MDI3MDU0MjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYyNzZmODYzMmM0ZWUzYTMyNTFlYzVmZWYzNDdhZTFkYzQ1ZmNiMjE3MjJkZjA4ZjRiYjQ1NTVhZjVhZjAzMDcifQ" + } + ] + }, + { + "id": "claim_06aadf17726d432894978250d3e768d6", + "statement": "Test stub helpers (stubs.py) provide _any_id_ai_message, _any_id_ai_message_chunk, _any_id_human_message, and AnyStr class to match messages with any ID value, enabling assertions on message content independent of generated UUIDs.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/stubs.py", + "version": "repo-file-v1:sha256:3d42d384f2ef72708c96fc0dd013c0571cbf102d84024d4fd0f6b511ad1185c0" + } + ] + }, + { + "id": "claim_6e34471dd7954dc1967350cf8e8bdcea", + "statement": "@pytest.mark.parametrize allows testing multiple input/output combinations in a single test function, reducing code duplication and enabling clear failure isolation when one parameter set fails.", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L13-L20", + "version": "repo-lines-v1:sha256:ab8103f9ee7c85d9fb1c9d3d0cbe6264d1384aac292f90c74f84c77c06ed5398:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDliMzBjZTkzZjhkMmVlNmQ2NDdlYjNhN2RhYjQ0NjRiNWQ5Yjc5ZGExOTI4ZGE2MDY4MDQ3M2QyY2Y0ZTY4MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTdmYmQ4MDQxMWFjZWNkZDkxOGI4ODk4NDE3MTg3MGY5NWU4ZjE3YTZiNDA0YmU4MDY0YmE3NmJhNjc2NDk3NyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjQ2M2RlMzg2ZTA3OTFhZGQ1MmUzZjZlM2EwOWYyYmY2NDhmZTQzMTMxOGUxOTE1YjdlNWRlOTlmN2E1ODQ1MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzgzNzQ1ZTU1YTIzYmI0MzZjYTUxMGFiMTgwNGQyODMwMjhhNTJlNzc5MjE5YmNlNzAyODRkOWY3YjEwMDcxMCJ9" + } + ] + }, + { + "id": "claim_176e1b33a51245668c26568a36aa346c", + "statement": "Syrupy snapshot testing captures complex output (traces, serialized objects, nested structures) and compares against baseline snapshots, with snapshots stored in __snapshots__/ directories and updated via pytest --snapshot-update or make test_watch.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/runnables/test_runnable.py#L1-L50", + "version": "repo-lines-v1:sha256:28f44b5941f0ec19fc995d7f3313b8eb55ecae1b3a6d383f4d96d598f5620f16:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAzNGM5Y2RkNDJmOGQ4OGMwNDQyNTM3M2E3MzM2MjA5ZjZiMmEyZGNlYmVjMzJiNGQ0MDU5M2Y5MGJlMmJiMjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MTUyOWViMzIyNzY4NTdiMjVmNmVkMzYzNWJjOGE3YjU2ODI1NzA4YmY0OWZiNTFlMWY3YThhNWZlMjQyMTAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImYxYTc5Y2ZkNWJhMzg1ZTcyZDI1YTRjMWUzNDBlMjk5M2FhZmFhNTcyMmIyYmQ5Y2ZiOTgxMDdmNmYxNjQ2NDMifQ" + } + ] + }, + { + "id": "claim_f597397dfd2c414b8908e7ba6f7ce8c9", + "statement": "CallbackManager and CallbackManagerForToolRun enable tests to inject FakeCallbackHandler to verify callback firing, chain execution order, and event counts without side effects.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/callbacks/test_async_callback_manager.py#L22-L75", + "version": "repo-lines-v1:sha256:c7ee525cbf75243640d9986b7a26d73489005f5142d7bd74af88598b9d563daa:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFiYjQzZjQwMmVhN2UxYjA3NTU3YjdhYTRjZDFjYjlhZDIxMjMxNmRhYWMwZDBlN2U0MTg0Mzg0NmJlM2MwZDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiMjg0YzA1OTJjMDQzZGE1N2EyYmM0ODNmYWFhZDU1OTE4ZmUwZmU5YmY5NDFmMzQ2NzdlOGY3OTk1MjE1OWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5OGIwNjNkOWI0N2U2NTM3OTYyNTU3MmNjZTE5NzY2OWQ1NzY3ZTEyYzE4YmUwNzU2NTJiOGNkOTgxY2U4ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVjOGJjNjA3OTc4ZWEzMWU3Y2M2ODVlNzhhZWM3MDc3OTVhZWFkNDc3N2M4OWIzYmUzYWYyZTVlNjMwZWI0YTIifQ" + } + ] + }, + { + "id": "claim_b7009493c8cb4da793e4e9b18abf3480", + "statement": "Type checking via mypy is integrated into 'make lint' alongside ruff checks and format validation; mypy checks all Python files in the package and is run with --cache-dir to speed repeated checks.", + "evidence": [ + { + "resource": "repo://libs/core/Makefile#L60-L67", + "version": "repo-lines-v1:sha256:933d456111de08e7749ab0a4195bf01d0fa573cccd86ef29d442e390779caa54:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWQyYzczODUyODNkZDhlODQzNjZjMDk5MWU5ODI2NWUyZWU1MmZjY2VhZTdhMDJmODE5YzI4NzA0ZjQ3MGJiZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjI2NGUxNzEyMGQxMDU3NjNiYTkzYjkyYWEwN2M3YzRmYjE5MzM0NzMzMWZlNWMwNDI5MmM5MDk4OTE5NWZmNSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZWE5YzU2ZjhiMTIzYjdiZjQ0ZjgxMjU4MTA4NmE4ZGU1YjBjYTk2NWZjMDYwYjAyZTA1MWFlOWU5NDMwODY1OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNWU2NDJiYTYwOTY2OTA4MDMzOTY3ZGE2OTUzZDdlYzkwMzM3YzkwMDZmNWJiZGVjMTIwZmZiZTM3ZTg5N2NiYSJ9" + } + ] + }, + { + "id": "claim_58af02bb87b043a38a43f54d9c0d4436", + "statement": "The runnables conftest.py fixture (_disable_local_langsmith_tracing) sets LANGSMITH_TRACING=false and removes developer LangSmith environment variables at session scope to keep runnable unit tests independent of local LangSmith configuration.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/runnables/conftest.py#L10-L37", + "version": "repo-lines-v1:sha256:fed3cc99412a88ab0c021986da4fe9a311939e8d66b9023fb7552c25d0937869:eyJzZWxlY3RlZExpbmVDb3VudCI6MjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ2ODgyNDQ3MDY4YzMzYjJmMzJlNDVmYWM0Y2UxYWZhYzQ5ODNkYzA3MzBlNmZhOWEwMzdlYWM5Y2ZhMjM3NzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNhYzMxNGZmOWYwZTBhOTU0NzNiNjlmYzcyMzA3MWI0MzU4ZGI1ZDQ4MmQ5YWM2NmI1MGQ5Y2IwZmJkZDRmMzQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5ZTQwMTYzYTRkMzhjNmFmNzg1OThmNzdkZTQ4ZWUzMzFlYzdhNTI0YzU4NmViY2QwYTA5YTkwZWY1Njg1MjAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + } + ] + }, + { + "id": "claim_655f1ee7e72547218e865ae9f01d6173", + "statement": "Async tests use async def and are marked with @pytest.mark.asyncio; tests await ainvoke() and astream() to verify async execution without blocking, enabling testing of concurrent model invocation and stream handling.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/fake/test_fake_chat_model.py#L39-L73", + "version": "repo-lines-v1:sha256:089d7cc7803d27b58295722b1a0ac24c4c7af09bc39df303b4fe1ed715112fed:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA2NzUxNjU1NGVjYjQzYzIxMjA3M2IyMzM2MTQ4OTc0NjFhMTBlMDQ0MjRjNWU5MDYyOWJjMjdmY2M1OTA1NjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY0YzVlMDIzMTJiNjVhZTU1M2IwNzEyMmJkMmQ4ZTM0OWIxMDBhYzYyYTY2ODY0MGQwMjAxNDk0MGMwMDk3MzYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI0MTU1YjJlMGE3NjA1ZWQ1NmM4ZWU2NDI3NjdhMWIwOTU4NjIwNTM3M2NkNWJmYzk3ZjIyODFlMzIyODhhN2UiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIwODE2YTliMmE2MzViYjIwMmY4MmY4NDM3YmZhOWRkMTJiZDI3NzQxNzE0OWMzOWFlMzRmZTM4MmM4ZWU0OWUifQ" + } + ] + }, + { + "id": "claim_290be57f0f2e489c8fe914e8716e6cbb", + "statement": "pydantic_utils.py provides schema normalization (_normalize_schema, _schema) to handle Pydantic v1 vs v2 differences in JSON schema format, ensuring tests work across Pydantic versions; skip_if_no_pydantic_v1 marks tests for Pydantic v1 compatibility.", + "evidence": [ + { + "resource": "repo://libs/core/tests/unit_tests/pydantic_utils.py", + "version": "repo-file-v1:sha256:c80ecf32bea0b4e7d5a86ae816614622e63f082e45bf2dc665118ddcb8400d87" + } + ] + } + ], + "verification": { + "by": "openwiki/0.5.0", + "at": "2026-09-03T15:18:34.589Z" + } +} diff --git a/openwiki/.last-update.json b/openwiki/.last-update.json new file mode 100644 index 0000000000..551d26b2c3 --- /dev/null +++ b/openwiki/.last-update.json @@ -0,0 +1,8 @@ +{ + "updatedAt": "2026-09-03T16:36:59.039Z", + "command": "init", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "model": "claude-haiku-4-5", + "status": "complete", + "language": "en" +} diff --git a/openwiki/.page-manifest.json b/openwiki/.page-manifest.json new file mode 100644 index 0000000000..6bed8af05b --- /dev/null +++ b/openwiki/.page-manifest.json @@ -0,0 +1,166 @@ +{ + "schemaVersion": 1, + "pages": { + "/openwiki/agent-execution.md": { + "pageVersion": "sha256:bd16b5e2d2f508d0aba8962fb1e836569b2a9af1615a3ec3a278948b6b703043", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/agent-factory.md": { + "pageVersion": "sha256:861e60569112cc57094912804e603c9cd7bd4162df709645dd543bb46b2cca3f", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/architecture.md": { + "pageVersion": "sha256:4d075dd399f2c74cd6a1d03563dd5d1695887843ca5c5b920422838e41870e43", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/callbacks.md": { + "pageVersion": "sha256:a3d31bdc7065f8a6bb1a5dc81e82f5b216c238dffdf3e1c523d8a0ed7cf0558d", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/chat-models.md": { + "pageVersion": "sha256:d6bff3b81a95d8ca6bc4bdbd9b5c49d4db94d4b506fe48361a3571a0a8f2a35f", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/ci-workflows.md": { + "pageVersion": "sha256:8af0cd4e2f4fde3bbeaa007c68ac7b8049bd352e75e6455e2b7896818da707cc", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/composability.md": { + "pageVersion": "sha256:2686a50367f151aef17fad64706fe0e5bb3d6d279febeecbac7d3760d2d17424", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/dev-commands.md": { + "pageVersion": "sha256:7dd5ad8767d8a3f658ed06a962ccaafe416623877d4864ce71de856a4ae7b52f", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/integration-tests.md": { + "pageVersion": "sha256:8bf3d528d2fe2628f24d10c54b6445ad11f8ec7abae99049815d037c83fdaf08", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/mcp-integration.md": { + "pageVersion": "sha256:39684cc4e8be6978f803b85b99406b6a877eaf9e8c8b964279a413f854340751", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/messages.md": { + "pageVersion": "sha256:64e569ccd662acb76519938c6e8867f0e82285557634fc1a08205e1d12e2dcee", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/middleware.md": { + "pageVersion": "sha256:edd3a6070069752c8ce3d79e8b4fb6c0e58c7fa04f5bf7b55223db4d14b269a5", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/model-initialization.md": { + "pageVersion": "sha256:0ff4006082d29f33169349313c83cc890f4fafd73f0a3df4a51cf88689471825", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/openai-provider.md": { + "pageVersion": "sha256:13741eab09e2d769cdfd885ea9b92e8a5f790a63498d6b10bf6cfeeaecf076b1", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/partner-pattern.md": { + "pageVersion": "sha256:989b7aec4a438448775477cdd98e554b1cc64e2e0ec441dc7eb1e2ea3e406d1d", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/prompts.md": { + "pageVersion": "sha256:5f946f7b6b3214808e71060f1626dc4100caf473eb5e0186d8064f20219062b9", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/quickstart.md": { + "pageVersion": "sha256:7dc36350c57797f4e4cb42c4ca2523c8a6fda499d2176362da99865392c8721b", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/runnables.md": { + "pageVersion": "sha256:2785275fb70363fa2ba7b5c6b351f08d2bb5de58d88dec14a5c66d33f670ea88", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/source-map.md": { + "pageVersion": "sha256:3b35861ca57c848ef54b88056e826fb29f242c92d428ce186f1ae6001df177ca", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/streaming.md": { + "pageVersion": "sha256:a438de3bacc6da5a8e98bfd85d8e6f3a947deaf987bccf74fa6efdf166987bb6", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/structured-output.md": { + "pageVersion": "sha256:94885a8043502300eae424ea3f016be44eb925bbaf0ad6297fdd741c3280ee69", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/tools.md": { + "pageVersion": "sha256:f95633f03549fc6949b4ba8fc5d1bd13a03843a429ff10fa3284ae68f6ffce88", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + }, + "/openwiki/unit-tests.md": { + "pageVersion": "sha256:7f15b0e6bb2c95e703a2e0c8f6fe04091727b2cb26ae3a3260124cd43f1394e0", + "completedBy": "openwiki/0.5.0", + "completedRunId": "b9d6bfc2-1d6c-4466-8f2b-ecd535b3e562", + "gitHead": "1e6a4f0b45b60475f566f3ad49fb32ea99d22233", + "sourceFingerprint": "sha256:864ce30170c730feb10d06b6ec7be97c50c81be23ef4369a1a61a0d3804ce2e8" + } + } +} diff --git a/openwiki/INSTRUCTIONS.md b/openwiki/INSTRUCTIONS.md new file mode 100644 index 0000000000..b0e3e331fe --- /dev/null +++ b/openwiki/INSTRUCTIONS.md @@ -0,0 +1 @@ +A code wiki for this local repository. Prioritize a concise quickstart, architecture overview, source map, key workflows, domain concepts, operations/runbook notes, testing guidance, and integration points. Inspect git history to understand reasoning behind code changes and the progression of the repository. Keep pages grounded in the repository structure and recent code changes. Prefer practical navigation for engineers over generic summaries. diff --git a/openwiki/agent-execution.md b/openwiki/agent-execution.md new file mode 100644 index 0000000000..7878b83550 --- /dev/null +++ b/openwiki/agent-execution.md @@ -0,0 +1,477 @@ +--- +type: Agent Runtime Architecture +title: Agent Execution Flow and Loop Control +description: Traces the runtime lifecycle of an agent from user input through model invocation, tool dispatch, and loop termination conditions, with detailed state management and middleware integration points. +tags: [agent-execution, control-flow, state-machine, loop-control, tool-dispatch, middleware, langchain] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-03e8ca0eebe37feda8566793 + resource: repo://libs/langchain_v1/langchain/agents/middleware/types.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +Agent execution in LangChain follows a structured, state-driven loop orchestrated by a LangGraph StateGraph. The agent repeatedly invokes a language model, processes tool calls, and decides whether to continue the loop or terminate based on model output, tool execution results, and middleware directives. + +### Core Execution Pattern + +The agent execution flow consists of five main phases: + +1. **Initialization** – User input arrives and enters the graph via START +2. **Model Call** – The language model is invoked with the current message state +3. **Tool Dispatch & Execution** – Model-requested tools are executed in parallel or sequentially +4. **Result Processing** – Tool results are wrapped in `ToolMessage` objects and added to state +5. **Termination Check** – The loop exits or cycles based on tool calls in the model response and configured stop conditions + +```mermaid +sequenceDiagram + participant User + participant Graph as Agent Graph + participant MW as Middleware + participant Model as Language Model + participant Tools as Tool Node + participant Result as Result Processing + + User->>Graph: invoke(messages=[...]) + Graph->>MW: before_agent() + MW-->>Graph: state updates + Graph->>MW: before_model() + MW-->>Graph: state updates + Graph->>Model: Call with current messages + Model-->>Graph: AIMessage with tool_calls + Graph->>MW: after_model() + MW-->>Graph: state updates + + alt Model called tools + Graph->>Tools: Execute tool_calls in parallel + Tools->>Result: Invoke each tool + Result-->>Tools: ToolMessage results + Tools-->>Graph: List of ToolMessages + Graph->>Graph: Append ToolMessages to state + Graph->>Graph: Check termination condition + + alt Continue loop + Graph->>MW: before_model() again + MW-->>Graph: state updates + Graph->>Model: Call again with tool results + else Exit loop + Graph->>MW: after_agent() + MW-->>Graph: state updates + Graph->>User: Return final messages + end + else No tools called + Graph->>Graph: Exit condition met + Graph->>MW: after_agent() + MW-->>Graph: state updates + Graph->>User: Return final messages + end +``` + +Flow showing user input, middleware hooks, model invocation, tool execution, and loop termination. + +## Agent State + +The agent maintains a typed `AgentState` dictionary that accumulates execution history and configuration: + +### State Structure + +```python +class AgentState(TypedDict, Generic[ResponseT]): + """State schema for the agent.""" + + messages: Required[Annotated[list[AnyMessage], add_messages]] + jump_to: NotRequired[Annotated[JumpTo | None, EphemeralValue, PrivateStateAttr]] + structured_response: NotRequired[Annotated[ResponseT, OmitFromInput]] +``` + +**Key fields:** + +- **`messages`**: A reducer-based list of all messages in the conversation. Uses `add_messages` to accumulate `UserMessage`, `AIMessage`, and `ToolMessage` objects rather than replacing them. This forms the conversation history passed to the model on each iteration. + +- **`jump_to`**: An ephemeral middleware control field (not persisted) used by `before_model` and `after_model` hooks to redirect execution to `'tools'`, `'model'`, or `'end'` nodes, overriding the default loop logic. + +- **`structured_response`**: When `response_format` is configured on the agent, this field holds the parsed structured output from the last model invocation or tool call. Cleared explicitly when a new iteration begins without a structured response. + +### Message Encoding + +Messages flow through the system as `langchain_core.messages` objects: + +- **`AIMessage`**: Emitted by the model, may contain `tool_calls` (list of dicts with `id`, `name`, `args`). +- **`ToolMessage`**: Result of tool execution, carries `tool_call_id` to link it back to the model's request, `name` of the tool, and `content` with the tool's output. +- **`UserMessage`**, **`SystemMessage`**: User and system prompts; system message is prepended at model call time. + +## Model Request and Response + +### ModelRequest + +Before invoking the model, the agent constructs a `ModelRequest` object that encapsulates all inputs: + +```python +@dataclass(init=False) +class ModelRequest(Generic[ContextT]): + model: BaseChatModel + messages: list[AnyMessage] # excluding system message + system_message: SystemMessage | None + tool_choice: Any | None + tools: list[BaseTool | dict[str, Any]] + response_format: ResponseFormat[Any] | None + state: AgentState[Any] + runtime: Runtime[ContextT] + model_settings: dict[str, Any] = field(default_factory=dict) +``` + +The request is passed to `wrap_model_call` middleware handlers so they can intercept, retry, modify, or cache the model call before invoking the actual model. + +### ModelResponse + +The core model execution returns a `ModelResponse`: + +```python +@dataclass +class ModelResponse(Generic[ResponseT]): + result: list[BaseMessage] + structured_response: ResponseT | None = None +``` + +The `result` list typically contains a single `AIMessage`, but may include additional `ToolMessage` objects if a structured output tool was invoked. The `structured_response` field holds the parsed schema when `response_format` is configured. + +### Extended Model Response + +Middleware can return an `ExtendedModelResponse` to attach an optional `Command` for additional state updates: + +```python +@dataclass +class ExtendedModelResponse(Generic[ResponseT]): + model_response: ModelResponse[ResponseT] + command: Command[Any] | None = None +``` + +Commands are applied via the graph's reducers, so messages in commands are **added alongside** (not replacing) the model response messages. + +## Middleware Integration + +The agent execution pipeline is instrumented with middleware hooks that run at specific lifecycle points, enabling cross-cutting concerns like logging, caching, error handling, and dynamic tool injection. + +### Hook Lifecycle + +Middleware methods are invoked at these phases: + +1. **`before_agent(state, runtime) -> dict | None`** – Runs once at the very start, before model initialization. Useful for setup or initial state configuration. + +2. **`before_model(state, runtime) -> dict | None`** – Runs before each model invocation, including after tool execution. Middleware can modify the state or jump to `'tools'`, `'model'`, or `'end'`. + +3. **`wrap_model_call(request, handler) -> ModelResponse | AIMessage | ExtendedModelResponse`** – Intercepts the actual model call. Middleware receives a handler callback, can invoke it multiple times (for retry logic), skip it (for short-circuit caching), or modify the request before calling. Executes as part of the model node, before `after_model`. + +4. **`after_model(state, runtime) -> dict | None`** – Runs after the model returns and messages are added to state. Middleware can inject synthetic `ToolMessage` objects, modify state, or jump. + +5. **`after_agent(state, runtime) -> dict | None`** – Runs once at the very end, after the loop exits. Useful for final cleanup, summarization, or post-processing before returning to the user. + +### Middleware Composition + +Multiple middleware instances are chained in registration order. For hooks like `before_model`, each middleware runs sequentially, with state updates flowing forward. For `wrap_model_call`, middleware compose as nested handlers, with the first in the list becoming the outermost layer that wraps all subsequent middleware. + +**Middleware tool calls:** + +Middleware can register additional tools via the `tools` class attribute. These are merged into the `ToolNode` and are available for the model to call. + +**Middleware jump control:** + +A middleware's `before_model` or `after_model` method can use the `@hook_config(can_jump_to=['tools', 'model', 'end'])` decorator to declare which destinations it may jump to. If a method sets `state['jump_to'] = 'end'`, the graph will exit the loop immediately rather than continue to tools or another model iteration. + +## Loop Control and Termination + +The agent loop is governed by conditional edges that inspect the model's `AIMessage` and the current state. Termination conditions are checked after the model returns and after tool execution completes. + +### Model-to-Tools Decision (_make_model_to_tools_edge) + +After the model is invoked, the graph checks whether to dispatch tools: + +1. **Explicit Jump**: If `state['jump_to']` is set by middleware, use that destination. +2. **No AIMessage**: If the message list is empty or corrupted, exit. +3. **No Tool Calls**: If the last `AIMessage` has an empty `tool_calls` list, exit the loop (model decided not to use tools). +4. **Pending Tool Calls**: Filter out tool calls that have already been executed (matched by `tool_call_id`) and structured output tool calls. If pending calls remain, dispatch them as `Send` commands to the tools node. +5. **Synthetic Tool Messages**: If an `AIMessage` has tool calls but all have been executed or are structured, the loop jumps back to the model to process injected `ToolMessage` results. +6. **Structured Response Ready**: If `state['structured_response']` is now populated (a structured output tool was executed), exit. + +### Tools-to-Model Decision (_make_tools_to_model_edge) + +After tool execution completes: + +1. **No AIMessage**: If the message list is corrupted, jump to model for recovery. +2. **Return Direct Tools**: If all executed client-side tools have `return_direct=True`, exit the loop immediately. +3. **Structured Output Executed**: If any executed tool is a structured output tool, exit (the response is ready). +4. **Default**: Continue the loop, jumping back to `before_model` so the model can process tool results. + +### Model-to-Model Decision (_make_model_to_model_edge) + +When structured output tools are configured but no regular tools exist, the model invokes itself in a loop until a structured response is successfully parsed: + +1. **Explicit Jump**: Check `state['jump_to']`. +2. **Structured Response Ready**: If `state['structured_response']` is set, exit. +3. **Default**: Jump back to model to retry (e.g., after a structured output validation error). + +### Termination Conditions Summary + +The loop terminates when any of these are true: + +- Model does not call any tools (`tool_calls` is empty). +- Model jumps via middleware to `'end'`. +- All pending tool calls are structured output tool calls (response is ready). +- A structured output tool is executed (response is ready). +- A tool with `return_direct=True` is executed. +- An explicit exception is raised and not caught. + +## Tool Execution + +When the model requests tools, the `ToolNode` executes them. This node is responsible for: + +1. **Receiving tool calls**: Unpacked from the latest `AIMessage`. +2. **Looking up tools**: By name in the `tools_by_name` registry. +3. **Parallel execution**: Tools are invoked concurrently when possible. +4. **Wrapping results**: Each tool result becomes a `ToolMessage`. + +### Tool Call Request and Response + +Tools are invoked via the `wrap_tool_call` middleware interception point: + +```python +class ToolCallRequest: + tool_call: dict # {"id": "...", "name": "...", "args": {...}} + tool: BaseTool + state: AgentState[Any] + runtime: Runtime[ContextT] +``` + +Middleware can intercept with `wrap_tool_call(request, handler)` to: +- Retry on failure (call `handler` multiple times). +- Validate or modify arguments. +- Cache results. +- Skip execution (return a synthetic `ToolMessage`). +- Throw custom exceptions. + +The handler returns a `ToolMessage` or `Command` that is added to state. + +### Structured Output Tools + +When `response_format` is configured with `ToolStrategy`, a synthetic tool is created for each schema in the response format. These tools encode the structured output as arguments. When invoked: + +1. The tool call is intercepted in the model output handler. +2. Arguments are parsed and validated against the schema. +3. The parsed object is stored in `state['structured_response']`. +4. A `ToolMessage` is synthesized to acknowledge the call. +5. The loop terminates (structured response is ready). + +If validation fails and `handle_errors` is configured on the strategy, a synthetic `ToolMessage` with an error is injected, and the loop continues so the model can retry. + +## Graph Structure + +The agent graph is built dynamically by `create_agent()` with nodes and conditional edges: + +### Nodes + +- **`model`**: Invokes the language model with middleware hooks; returns a `Command` updating `messages` and optionally `structured_response`. +- **`tools`** (optional): Present only if tools are configured; executes tool calls in parallel and returns `ToolMessage` objects. +- **`.before_agent`**: Middleware's `before_agent` hook; runs once at start. +- **`.before_model`**: Middleware's `before_model` hook; runs before each model invocation. +- **`.after_model`**: Middleware's `after_model` hook; runs after each model invocation. +- **`.after_agent`**: Middleware's `after_agent` hook; runs once at end. + +### Entry, Loop, and Exit Points + +- **Entry Node** (START → ?): First node to run, determined by middleware presence. If middleware has `before_agent`, it runs first. Otherwise, if middleware has `before_model`, that runs first. Otherwise, jump straight to `model`. + +- **Loop Entry Node** (tools → ?): Where the loop jumps back after tool execution. Typically `before_model` if present, else `model`. + +- **Loop Exit Node** (model → ?): Where the conditional edge for tool dispatch originates. Typically the last `after_model` middleware if present, else `model`. + +- **Exit Node** (?→ END): Last node to run before returning to user. If middleware has `after_agent`, that runs last. Otherwise, exit immediately. + +### Conditional Edges + +- **Model-to-Tools/Loop**: From `loop_exit_node`, decide whether to dispatch tools, continue the loop, or exit, based on the model's tool calls and structured response state. +- **Tools-to-Model/Loop**: From `tools` node, decide whether to continue the loop or exit, based on tool results and `return_direct` flags. +- **Middleware Jumps**: From any middleware node with a `can_jump_to` configuration, conditionally route based on `state['jump_to']`. + +## Structured Output Processing + +When `response_format` is supplied to `create_agent()`, the agent handles structured output via one of two strategies: + +### Tool Strategy + +A synthetic tool is added for each schema in the response format. The model is encouraged to call this tool to provide structured output. + +**Flow:** +1. Model is bound with the structured output tool. +2. When model invokes the tool, the agent parses arguments against the schema. +3. If valid, parse result → `state['structured_response']`, synthesize `ToolMessage`. +4. If invalid and `handle_errors=True`, inject error message, model retries. +5. Loop exits when structured output is successfully parsed. + +**Advantages:** Works with any model; validates at parse time. + +**Disadvantages:** Requires an extra model invocation. + +### Provider Strategy + +The model's native structured output API (e.g., OpenAI's `response_format`) is used directly. + +**Flow:** +1. Model is configured with provider-specific structured output parameters. +2. Model returns structured data in its response (no tool call). +3. Agent parses the model's output against the schema. +4. Loop exits; no tool invocation needed. + +**Advantages:** Faster (one invocation); native support. + +**Disadvantages:** Provider-specific; not available for all models. + +**Auto-Detection:** +When `response_format` is a raw schema, `create_agent()` auto-detects the best strategy at graph compile time based on model capabilities. If the model supports provider strategy, use it; otherwise fall back to tool strategy. + +## Callbacks and Monitoring + +At each major step, LangGraph fires callbacks and traces to `langsmith` for monitoring and debugging: + +- **Before model call**: `before_model` hooks, then `wrap_model_call` invocation. +- **After model call**: Model response added to state, then `after_model` hooks. +- **Tool execution**: Each tool call wrapped by `wrap_tool_call` hooks. +- **State updates**: Every `Command` returned from a node updates the graph state. + +Middleware can configure a `trace_policy` to shape what is recorded (e.g., `omit_payload` to drop sensitive data from traces while preserving timing and node names). + +## Message Accumulation and State Reducers + +The `messages` field uses a reducer function (`add_messages`) to accumulate rather than replace. This means: + +- When a node returns `{"messages": [new_msg]}`, the `add_messages` reducer **appends** `new_msg` to the existing list. +- Calling the model multiple times does not lose prior conversation history. +- Each `ToolMessage` is appended after its corresponding tool execution. +- The full conversation is always visible to the next model invocation. + +Other state fields like `structured_response` and `jump_to` are replaced, not accumulated. + +## Error Handling + +### Model Invocation Errors + +Exceptions during model invocation propagate unless `wrap_model_call` middleware catches them. A middleware can implement retry logic by catching exceptions and calling the handler again with a modified request. + +### Tool Execution Errors + +By default, exceptions during tool execution propagate. The `ToolNode` accepts a `handle_tool_errors` parameter to return error messages instead of crashing. Middleware can wrap tools with `wrap_tool_call` to implement custom error strategies. + +### Structured Output Validation Errors + +If a structured output tool's arguments fail to parse: +1. If `handle_errors=True` on the `ToolStrategy`, synthesize a `ToolMessage` with the error. +2. If `handle_errors=False`, raise `StructuredOutputValidationError`. +3. The loop continues (or exits) based on the strategy configuration. + +## State Machine View + +```mermaid +stateDiagram-v2 + [*] --> BeforeAgent: START + BeforeAgent --> BeforeModel: state updates applied + BeforeModel --> ModelCall: state updates applied + ModelCall --> AfterModel: AIMessage returned + AfterModel --> CheckToolCalls: state updates applied + + CheckToolCalls --> DispatchTools: pending tool calls exist + CheckToolCalls --> StructuredReady: structured response ready + CheckToolCalls --> EndLoop: no tool calls, no jump + + DispatchTools --> ExecuteTools: send tool call requests + ExecuteTools --> ToolsComplete: all tools executed + ToolsComplete --> CheckReturn: evaluate exit conditions + + CheckReturn --> EndLoop: return_direct or structured tool + CheckReturn --> BeforeModel: continue loop + + StructuredReady --> EndLoop: (implicit, structured output ready) + + EndLoop --> AfterAgent: exit condition met + AfterAgent --> [*]: return final state to user + + note right of ModelCall + wrap_model_call middleware runs here + may intercept, retry, or short-circuit + end note + + note right of DispatchTools + ToolNode executes tools in parallel + wrap_tool_call middleware can intercept each + end note + + note right of CheckToolCalls + Conditional edges check: + - explicit jump_to + - structured response + - pending tool calls + - return_direct flags + end note +``` + +State machine showing the progression from agent start through model invocation, tool dispatch, loop evaluation, and final exit. + +## Integration with Related Components + +- **Middleware** (`/openwiki/middleware.md`): Details on how middleware hooks compose and intercept at each phase. +- **Structured Output** (`/openwiki/structured-output.md`): In-depth guide to response formats, strategies, and schema validation. +- **Messages** (`/openwiki/messages.md`): Message types, serialization, and conversation management. +- **Agent Factory** (`/openwiki/agent-factory.md`): How `create_agent()` constructs the StateGraph from configuration. + +## Configuration and Operations + +### Recursion Limit + +The graph is compiled with `recursion_limit=9_999` to allow very long agent loops (hundreds of tool calls). This prevents premature termination while still protecting against infinite loops. + +### Checkpointing and Interrupts + +The agent graph can be compiled with a `Checkpointer` to persist state between invocations, and `interrupt_before`/`interrupt_after` lists to pause execution at specific nodes for human-in-the-loop workflows. + +### Debug Mode + +Passing `debug=True` to `create_agent()` enables verbose logging of node execution, state updates, and edge traversals, useful for understanding the control flow during development. + +## Example: Multi-Turn Agent with Tool Retry + +```python +from langchain.agents import create_agent, AgentMiddleware +from langchain.agents.middleware.types import ModelRequest + +class RetryMiddleware(AgentMiddleware): + def wrap_model_call(self, request, handler): + for attempt in range(3): + try: + response = handler(request) + # Check if response has tool calls + if response.result and response.result[0].tool_calls: + return response + # No tool calls on success, return + return response + except Exception as e: + if attempt == 2: + raise + # Retry by calling handler again + +agent = create_agent( + model="anthropic:claude-sonnet-4-5-20250929", + tools=[my_tool1, my_tool2], + middleware=[RetryMiddleware()], + system_prompt="You are a helpful assistant that uses tools." +) + +# Invoke with a user message; loop runs until no tools are called or error occurs +result = agent.invoke({"messages": [{"role": "user", "content": "Help me with X"}]}) +for msg in result["messages"]: + print(f"{msg.type}: {msg.content}") +``` + +This example shows how middleware intercepts the model call to implement retry logic that re-invokes the handler on failure. diff --git a/openwiki/agent-factory.md b/openwiki/agent-factory.md new file mode 100644 index 0000000000..b8f21f4934 --- /dev/null +++ b/openwiki/agent-factory.md @@ -0,0 +1,599 @@ +--- +type: "Reference" +title: "Create a basic agent" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-07e634f5cd5f00c636010306 + resource: repo://libs/langchain_v1/langchain/agents/middleware/__init__.py + - id: openwiki-source-4ed5b553d7dea01d659161d1 + resource: repo://libs/langchain_v1/langchain/agents/middleware/_trace_policy.py + - id: openwiki-source-03e8ca0eebe37feda8566793 + resource: repo://libs/langchain_v1/langchain/agents/middleware/types.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +The **Agent Factory** is the foundational entry point for building LangChain agents. The `create_agent` function constructs a compiled LangGraph state machine that orchestrates conversation flow between a language model, tool execution, and pluggable middleware layers. It handles tool binding, structured output, state schema resolution, and middleware composition automatically, allowing developers to focus on business logic while the factory manages the complex graph construction and execution model. + +## Quick Start + +```python +from langchain.agents import create_agent +from langchain_openai import ChatOpenAI + +def check_weather(location: str) -> str: + """Return the weather forecast for the specified location.""" + return f"It's sunny in {location}" + +# Create a basic agent +agent = create_agent( + model="openai:gpt-4o", + tools=[check_weather], + system_prompt="You are a helpful weather assistant." +) + +# Stream responses +inputs = {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]} +for chunk in agent.stream(inputs, stream_mode="updates"): + print(chunk) +``` + +## Agent Architecture + +The agent factory constructs a **state machine graph** with the following structure: + + +```text +graph TD + START["START"] --> ENTRY["Entry Node
(before_agent)"] + ENTRY --> LOOP_ENTRY["Loop Entry
(before_model | model)"] + LOOP_ENTRY --> MODEL["Model Node
(LLM Call)"] + MODEL --> AFTER_MODEL["After Model
(middleware)"] + AFTER_MODEL --> ROUTER{Has Tool Calls?} + ROUTER -->|Yes| TOOLS["Tools Node
(Execute Tools)"] + ROUTER -->|No| EXIT["Exit Node
(after_agent)"] + TOOLS --> TOOLS_ROUTER{Tool Direct Return?} + TOOLS_ROUTER -->|No| LOOP_ENTRY + TOOLS_ROUTER -->|Yes| EXIT + EXIT --> END["END"] +``` + +**Key Nodes:** + +- **Entry Node**: Runs `before_agent` hooks once at the start of the conversation. +- **Loop Entry**: Begins each iteration of the model → tool loop. Runs `before_model` middleware. +- **Model Node**: Calls the language model with messages and system prompt. Handles structured output parsing. +- **After Model**: Runs `after_model` hooks after model output (runs each loop iteration). +- **Tools Node**: Executes tools returned by the model. Skipped if no tools are defined. +- **Exit Node**: Runs `after_agent` hooks once at the end of the conversation. + +## Core Concepts + +### AgentState + +The agent maintains a typed state dictionary that flows through the graph: + +```python +class AgentState(TypedDict): + messages: list[AnyMessage] # Conversation history + jump_to: JumpTo | None # Optional control flow override + structured_response: ResponseT | None # Parsed structured output (if enabled) +``` + +**Reducers and Aggregation:** +- `messages` uses `add_messages` reducer: new messages are merged with existing ones, with duplicates by `id` being replaced. +- `jump_to` is ephemeral: set by middleware to override default routing (e.g., "model", "tools", "end"). +- `structured_response` is cleared each iteration unless explicitly set, preventing stale values after checkpointing. + +### Input and Output Schemas + +The factory derives input and output schemas from the base `AgentState` and any middleware-provided schemas: + +```python +class InputAgentState(TypedDict): + messages: list[AnyMessage | dict[str, Any]] # User can pass plain dicts + +class OutputAgentState(TypedDict): + messages: list[AnyMessage] + structured_response: ResponseT | None # Only if response_format is set +``` + +Middleware can extend state by declaring a `state_schema` attribute (a `TypedDict`), which is merged during graph construction. + +### ModelRequest and ModelResponse + +All middleware hooks operate on structured request/response objects: + +**ModelRequest** encapsulates everything needed for a model call: +- `model`: The `BaseChatModel` instance +- `messages`: Current conversation (excluding system message) +- `system_message`: Optional system prompt +- `tools`: Available tools to bind +- `response_format`: Structured output spec (if enabled) +- `state`: Current agent state +- `runtime`: LangGraph `Runtime` for accessing context +- `tool_choice`: Override tool selection behavior +- `model_settings`: Extra kwargs to pass to `model.bind()` + +Middleware can call `request.override(**changes)` to create a new request immutably, enabling request transformation before the model is invoked. + +**ModelResponse** carries the result: +- `result`: List of messages (usually one `AIMessage`, sometimes with `ToolMessage` for structured output) +- `structured_response`: Parsed structured output (if `response_format` was set and parsing succeeded) + +### Model Binding and Structured Output + +The factory handles three structured-output strategies: + +1. **ProviderStrategy**: Uses the model's native structured output (e.g., OpenAI's `response_format` param). Auto-detected for models with profile data indicating support. +2. **ToolStrategy**: Uses a special tool call to capture structured output. Tools are registered upfront; the model is forced to call the structured output tool to complete the turn. +3. **AutoStrategy**: Raw Pydantic schema that the factory auto-detects — converts to `ProviderStrategy` if the model supports it, otherwise `ToolStrategy`. + +When `response_format` is set, the factory: +- Creates `OutputToolBinding` instances wrapping the schema(s) +- Adds them as synthetic tools to the model binding +- After model output, parses tool calls matching the schema and extracts the structured response +- Prevents further tool execution if a structured output tool was called (exit condition) + +## Entry Point: create_agent + +The `create_agent(model, tools, ...)` function is the primary factory. Signature highlights: + +```python +def create_agent( + model: str | BaseChatModel, + tools: Sequence[BaseTool | Callable | dict] | None = None, + *, + system_prompt: str | SystemMessage | None = None, + middleware: Sequence[AgentMiddleware] = (), + response_format: ResponseFormat | type | dict | None = None, + state_schema: type[AgentState] | None = None, + context_schema: type[ContextT] | None = None, + checkpointer: Checkpointer | None = None, + store: BaseStore | None = None, + interrupt_before: list[str] | None = None, + interrupt_after: list[str] | None = None, + debug: bool = False, + name: str | None = None, + cache: BaseCache | None = None, + transformers: Sequence[TransformerFactory] | None = None, +) -> CompiledStateGraph[AgentState, ContextT, InputAgentState, OutputAgentState] +``` + +**Arguments:** + +- **model**: Model string (e.g., `"openai:gpt-4o"`) or `BaseChatModel` instance. String models are resolved via `init_chat_model`. +- **tools**: List of `BaseTool` instances, raw callables, or dict-based provider tools. `None` or empty creates a model-only agent. +- **system_prompt**: String or `SystemMessage` prepended to every model call. +- **middleware**: Ordered sequence of `AgentMiddleware` instances. Composing happens in list order (first = outermost). +- **response_format**: Structured output spec. Can be a Pydantic model, `ResponseFormat` subclass, or raw `dict` schema. +- **state_schema**: Custom state base class extending `AgentState`. Merged with middleware schemas; user's schema wins on conflicts. +- **checkpointer**: Thread-level persistence (e.g., chat memory across turns). +- **store**: Cross-thread persistence (e.g., user profiles, document stores). +- **interrupt_before/after**: Node names to suspend execution for user intervention. +- **debug**: Enable verbose logging. +- **name**: Graph name; used in LangSmith tracing and subgraph imports. +- **cache**: Execution cache (LangGraph feature). +- **transformers**: Additional stream transformer factories (e.g., for custom event filtering). + +## Middleware Composition + +Middleware extend agent behavior without modifying the core logic. They are composed into stacks at each intercept point. + +### Middleware Hooks + +Each `AgentMiddleware` can implement up to six hook methods: + +```python +class MyMiddleware(AgentMiddleware): + # Sync hooks (default) + def before_agent(self, state: AgentState, runtime: Runtime) -> dict | None: + """Runs once at start before any model calls.""" + return {"key": "value"} # Optional state updates + + def before_model(self, state: AgentState, runtime: Runtime) -> dict | None: + """Runs before each model call.""" + return None + + def wrap_model_call( + self, request: ModelRequest, handler: Callable + ) -> ModelResponse | AIMessage | ExtendedModelResponse: + """Wraps the model invocation itself (retry, fallback, caching, etc.).""" + return handler(request) # Call inner layer + + def after_model(self, state: AgentState, runtime: Runtime) -> dict | None: + """Runs after each model call.""" + return None + + def after_agent(self, state: AgentState, runtime: Runtime) -> dict | None: + """Runs once at the end after all iterations.""" + return None + + def wrap_tool_call( + self, request: ToolCallRequest, execute: Callable + ) -> ToolMessage | Command: + """Wraps tool execution (validation, retry, auth, etc.).""" + return execute(request) # Call inner layer +``` + +**Async Versions**: Prefix with `a` (e.g., `abefore_agent`, `awrap_model_call`). If only async is defined, sync invocation raises; if only sync is defined, async falls back. + +### Middleware Composition Rules + +- **Hook Chaining**: Each hook type (e.g., `before_model`) from all middleware is chained sequentially. +- **Order**: First middleware in the list becomes the outermost layer. + - Example: `middleware=[A, B, C]` → `A.before_model → B.before_model → C.before_model` + - For `wrap_*` hooks (outermost matters for retry/caching): `A.wrap_model_call(request, B.wrap_model_call(request, C.wrap_model_call(request, execute)))` +- **Sync/Async**: Sync and async paths are kept separate. Each hook can choose to implement sync, async, or both. The factory selects the appropriate variant at runtime. +- **Commands**: Middleware can return `Command` objects from `wrap_model_call` to update state (e.g., add synthetic tool messages). Commands accumulate inner-first and are applied after the model response. + +### Request/Response Immutability + +Middleware should use immutable patterns: + +```python +def wrap_model_call(self, request, handler): + # DON'T: request.tools = new_tools (deprecated, will warn) + # DO: + new_request = request.override(tools=new_tools) + return handler(new_request) +``` + +### Jump To Control Flow + +Middleware node hooks can override routing via the `jump_to` state field: + +```python +def before_model(self, state, runtime): + if should_skip_model_call(): + return {"jump_to": "end"} # Skip to end + return None +``` + +Valid destinations: `"model"`, `"tools"`, `"end"`. The hook method must declare `@before_model(can_jump_to=["end"])` to enable conditional routing. + +## Core Middleware + +The factory ships with a comprehensive middleware library: + +### Retry and Error Handling + +- **ModelRetryMiddleware**: Automatically retry failed model calls with exponential backoff. Configure max retries, exception types, and backoff factor. +- **ToolRetryMiddleware**: Retry failed tool executions. Supports custom failure policies (propagate, suppress, replace with error message). +- **ToolErrorMiddleware**: Convert selected tool exceptions to error `ToolMessage`s returned to the model (graceful error handling). + +### Model Variants and Fallback + +- **ModelFallbackMiddleware**: Fallback to alternate models on failure. Useful for resilience (e.g., try GPT-4o, fall back to Claude). + +### Tool-Related + +- **ToolCallLimitMiddleware**: Enforce maximum tool calls per turn or per agent run. +- **LLMToolSelectorMiddleware**: Use an LLM to pre-filter available tools based on the user query (reduce token cost and model confusion). +- **LLMToolEmulator**: Emulate tool calls without executing them (e.g., for testing or policy enforcement). +- **ProviderToolSearchMiddleware**: Automatically search for and register provider-native tools. + +### Execution and Sandbox + +- **ShellToolMiddleware**: Execute shell commands safely. Supports multiple execution policies: `HostExecutionPolicy` (local shell), `DockerExecutionPolicy` (containerized), `CodexSandboxExecutionPolicy` (remote sandbox). + +### Search and File Access + +- **FilesystemFileSearchMiddleware**: Search for files on the filesystem and return matches to the model. + +### Conversational Quality + +- **HumanInTheLoopMiddleware**: Pause execution to collect human feedback or approval before critical actions. +- **SummarizationMiddleware**: Automatically summarize long conversation histories to manage context length. + +### Data Protection + +- **PIIMiddleware**: Detect and redact personally identifiable information (PII). Configurable redaction rules; supports email, phone, SSN, API keys, and custom patterns. + +### Advanced + +- **ModelCallLimitMiddleware**: Limit total model invocations to prevent runaway loops. +- **ContextEditingMiddleware**: Edit or clear tool usage records in state (for context management). +- **TodoListMiddleware**: Maintain a persistent todo list across the conversation (custom state extension). + +## Tool Handling + +### Tool Registration + +Tools are registered at agent creation. Supported formats: + +1. **BaseTool instances** (preferred): Full control over execution, caching, and metadata. + ```python + from langchain_core.tools import tool + + @tool + def search(query: str) -> str: + """Search the web.""" + return ... + ``` + +2. **Raw callables**: Automatically wrapped into `BaseTool` instances. + ```python + def search(query: str) -> str: + """Search the web.""" + return ... + ``` + +3. **Dict-based provider tools**: Native tools from the model's provider (e.g., OpenAI's code interpreter). Not executed client-side; the provider handles them. + +### Tool Execution Flow + +1. Model returns `AIMessage` with `tool_calls` list. +2. Conditional routing checks for pending tool calls (not yet executed). +3. **ToolNode** batches pending calls and executes them in parallel (or sequentially, depending on config). +4. Execution results are wrapped in `ToolMessage`s and added to state. +5. Loop back to model unless: + - A tool with `return_direct=True` was executed + - A structured output tool was executed + - No pending tool calls remain + +### Dynamic Tools + +Middleware can add tools dynamically via `request.override(tools=[...])` in `wrap_model_call`. However, client-side execution requires either: +1. Tools registered upfront at agent creation, OR +2. Middleware implementing `wrap_tool_call` to execute dynamic tools + +If a tool is in the model's binding but not in the `ToolNode`, the factory raises `DYNAMIC_TOOL_ERROR_TEMPLATE` with guidance. + +## Structured Output Integration + +Structured output allows agents to return typed data alongside messages. The factory integrates three strategies: + +### ProviderStrategy (Preferred) + +Uses the model's native structured output (e.g., OpenAI's `response_format`): + +```python +from pydantic import BaseModel + +class WeatherReport(BaseModel): + location: str + temperature: int + conditions: str + +agent = create_agent( + model="openai:gpt-4o", + tools=[get_weather_tool], + response_format=WeatherReport, +) +``` + +The model is asked to return JSON matching the schema directly. Requires model support (auto-detected via profile or fallback patterns). + +### ToolStrategy + +Uses a special tool call to capture structure: + +```python +agent = create_agent( + model="anthropic:claude-sonnet-4-5", + tools=[get_weather_tool], + response_format=WeatherReport, +) +``` + +The factory creates a synthetic tool named after the schema (e.g., `WeatherReport`) and forces the model to call it with the desired data. The tool's arguments are parsed as the structured response. + +### AutoStrategy + +Automatically detects the best strategy: + +```python +agent = create_agent( + model=my_model, + response_format=WeatherReport, + # or response_format={"type": "object", ...} +) +``` + +If `response_format` is a raw Pydantic class or dict, the factory wraps it in `AutoStrategy`, which: +1. Checks the model's profile for native structured output support +2. If supported, converts to `ProviderStrategy` +3. Otherwise, uses `ToolStrategy` + +This conversion happens at model binding time, so middleware can override it by setting a different `ResponseFormat` in `wrap_model_call`. + +## State Schema Resolution + +The factory merges state schemas in this order: + +1. Middleware `state_schema`s (in registration order) +2. User-provided `state_schema` (if any) + +The user's schema wins on field conflicts. This allows: +- Middleware to extend state without forcing the user to know about it +- User to override middleware state field definitions (e.g., replace a field's reducer) + +Example: + +```python +class CustomState(AgentState): + user_id: str # Add custom field + +agent = create_agent( + model, tools, + state_schema=CustomState, + middleware=[SomeMiddleware()], # SomeMiddleware also extends state +) +``` + +The final graph uses a merged schema with all fields. + +## Graph Compilation and Execution + +Once the factory constructs the graph, it compiles it with LangGraph's `StateGraph.compile()`. This: + +- **Validates** node and edge definitions +- **Freezes** the schema and topology +- **Prepares** for execution (checkpointing, interrupts, etc.) +- **Returns** a `CompiledStateGraph` object + +### Execution Modes + +```python +# Synchronous streaming +for chunk in agent.stream({"messages": [...]}, stream_mode="updates"): + print(chunk) + +# Asynchronous streaming +async for chunk in agent.astream({"messages": [...]}): + print(chunk) + +# Blocking invocation +result = agent.invoke({"messages": [...]}) +``` + +### Checkpointing and Persistence + +Checkpointers persist state at each node boundary, enabling: +- **Chat memory**: Resume a conversation from any point +- **Human-in-the-loop**: Interrupt, inspect, and resume +- **Debugging**: Replay execution with modified state + +```python +from langgraph.checkpoint.sqlite import SqliteSaver + +checkpointer = SqliteSaver.from_conn_string(":memory:") +agent = create_agent( + model, tools, + checkpointer=checkpointer, + interrupt_before=["model"], # Pause before model calls +) + +# Invoke with a thread ID to save state +config = {"configurable": {"thread_id": "user_123"}} +result = agent.invoke({"messages": [...]}, config=config) + +# Resume later +result = agent.invoke({"messages": [...]}, config=config) # Resumes from checkpoint +``` + +## Tracing and Observability + +The factory integrates with LangSmith for tracing: + +- Each middleware hook is traced as a separate span +- Model calls are traced with inputs/outputs +- Tool executions are recorded +- Trace policies can filter sensitive data + +### Custom Trace Policies + +Middleware can declare a `trace_policy` to shape what is recorded: + +```python +from langgraph.types import TracePolicy, omit_payload + +class MyMiddleware(AgentMiddleware): + trace_policy = TracePolicy(process_inputs=omit_payload) + + def wrap_model_call(self, request, handler): + # This hook's span will not include request payloads + ... +``` + +A process-wide default can be set: + +```python +from langchain.agents.middleware import configure_trace_policy + +configure_trace_policy(TracePolicy(process_inputs=omit_payload)) +``` + +This applies to all agents created after the call, even those already instantiated. + +## Error Handling + +### Structured Output Errors + +If structured output parsing fails: +- `StructuredOutputValidationError`: Raised if the parsed JSON doesn't match the schema +- `MultipleStructuredOutputsError`: Raised if the model tried to return multiple structured outputs + +The `response_format`'s `handle_errors` parameter controls retry behavior: +```python +response_format = ToolStrategy( + schema=MySchema, + handle_errors=True, # Retry with error message + # or handle_errors="Custom error message" + # or handle_errors=(ValueError, TypeError) # Retry on these exceptions only +) +``` + +When retry is enabled, the factory adds an error `ToolMessage` to the state and loops back to the model. + +### Dynamic Tool Errors + +If middleware adds tools to `request.tools` that aren't in the client-side `ToolNode`: +- Factory raises `ValueError` with `DYNAMIC_TOOL_ERROR_TEMPLATE` +- Message includes registered tools and guidance on fixing it +- Mitigation: Either register tools upfront, or implement `wrap_tool_call` to execute dynamic tools + +## Extension Points + +Developers can customize agents via: + +1. **Custom middleware**: Subclass `AgentMiddleware` and implement desired hooks +2. **State extensions**: Declare `state_schema` to add custom fields +3. **Custom nodes**: Add nodes to the graph before compiling (advanced) +4. **Transformers**: Register stream transformers for event filtering (advanced) + +Example custom middleware: + +```python +from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse + +class LoggingMiddleware(AgentMiddleware): + def before_model(self, state, runtime): + print(f"Model call #{len(state['messages']) // 2}") + return None + + def wrap_model_call(self, request, handler): + print(f" Tools: {[t.name for t in request.tools]}") + response = handler(request) + print(f" Output: {response.result[0].content[:100]}...") + return response + +agent = create_agent( + model, tools, + middleware=[LoggingMiddleware()], +) +``` + +## Configuration Reference + +### Key Parameters Summary + +| Parameter | Type | Default | Purpose | +|-----------|------|---------|---------| +| `model` | `str \| BaseChatModel` | required | Language model for the agent | +| `tools` | `Sequence[...]` | `None` | Available tools | +| `system_prompt` | `str \| SystemMessage` | `None` | System context for model | +| `middleware` | `Sequence[AgentMiddleware]` | `()` | Behavior customization | +| `response_format` | `ResponseFormat \| type \| dict` | `None` | Structured output spec | +| `state_schema` | `type[AgentState]` | `None` | Custom state fields | +| `checkpointer` | `Checkpointer` | `None` | State persistence | +| `interrupt_before` | `list[str]` | `None` | Pause before these nodes | +| `interrupt_after` | `list[str]` | `None` | Pause after these nodes | +| `debug` | `bool` | `False` | Verbose logging | +| `name` | `str` | `None` | Graph identifier | + +## See Also + +- **[Middleware](/openwiki/middleware.md)**: Detailed middleware API and patterns +- **[Structured Output](/openwiki/structured-output.md)**: Deep dive on response schemas +- **[Tools](/openwiki/tools.md)**: Tool definition and integration +- **[Chat Models](/openwiki/chat-models.md)**: Model initialization and binding +- **[Agent Execution](/openwiki/agent-execution.md)**: Runtime behavior and streaming diff --git a/openwiki/architecture.md b/openwiki/architecture.md new file mode 100644 index 0000000000..981f522d97 --- /dev/null +++ b/openwiki/architecture.md @@ -0,0 +1,392 @@ +--- +type: "System Architecture" +title: "LangChain System Architecture" +description: "High-level decomposition of the LangChain framework into three layers: langchain-core (abstractions), langchain (orchestration and agents), and partners (provider integrations), showing dependencies, component responsibilities, and extension boundaries." +tags: [architecture, core, langchain, partners, orchestration, runnable, abstractions, layered-architecture] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-a1981e868973f6fd7f71e12e + resource: repo://libs/core/langchain_core/runnables/base.py + - id: openwiki-source-3486a94e6eb23a78271a5bfb + resource: repo://libs/core/pyproject.toml + - id: openwiki-source-788ee152ff67970aaacd6bb8 + resource: repo://libs/core/README.md + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-03e8ca0eebe37feda8566793 + resource: repo://libs/langchain_v1/langchain/agents/middleware/types.py + - id: openwiki-source-c479d4fffee5cf62576699e4 + resource: repo://libs/langchain_v1/langchain/chat_models/base.py + - id: openwiki-source-ba4876d385d4d18ed4fa0342 + resource: repo://libs/langchain_v1/pyproject.toml + - id: openwiki-source-b58f4da6042cc12c081038d5 + resource: repo://libs/langchain_v1/README.md + - id: openwiki-source-f4436232e0451a04247e92e5 + resource: repo://libs/langchain/pyproject.toml + - id: openwiki-source-680bcfbfa9eeccb5844443dd + resource: repo://libs/langchain/README.md + - id: openwiki-source-1e66a9da38565f8901e651f4 + resource: repo://libs/partners/openai/langchain_openai/__init__.py + - id: openwiki-source-738512768ef81ae009b097ac + resource: repo://libs/partners/openai/langchain_openai/chat_models/base.py + - id: openwiki-source-86b6689572ac828885d7d4b0 + resource: repo://libs/partners/README.md + - id: openwiki-source-7da6afe7fe64c6589cf1fed0 + resource: repo://libs/README.md +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +LangChain is organized as a **three-layer architecture** designed to separate concerns across abstraction, orchestration, and integration: + +1. **langchain-core**: Stable base abstractions for language models, tools, messages, runnables, and prompt templates. This layer is provider-agnostic and defines the contracts that the rest of the ecosystem implements. + +2. **langchain** (langchain-v1): High-level agent orchestration, middleware composition, and the Agent Factory. Built on top of LangGraph and langchain-core, it provides the primary user-facing interface for building agents and applications. + +3. **partners**: Provider-specific integrations (OpenAI, Anthropic, Ollama, etc.). Each partner package implements the core abstractions (BaseChatModel, embeddings, tools) and is released independently. + +This structure enables model interoperability, stable versioning, and independent provider evolution while keeping core abstractions stable across all implementations. + +## Dependency Flow + + +```text +graph TB + User["User Applications"] + + User -->|imports from| LangChain["langchain
(Orchestration & Agents)
v1.4.0"] + User -->|may use directly| Core["langchain-core
(Base Abstractions)
v1.6.1"] + + LangChain -->|depends on| Core + LangChain -->|depends on| LangGraph["LangGraph
(State Graph Engine)"] + + Partners["Partner Packages
(langchain-openai,
langchain-anthropic, etc.)"] + Partners -->|implement| Core + + User -->|optionally imports| Partners + LangChain -->|uses| Partners + + Classic["langchain-classic
(Legacy)
v1.0.8"] + Classic -->|depends on| Core + + style Core fill:#2d5016,stroke:#4a7c2c,color:#fff + style LangChain fill:#1f3a70,stroke:#3d5a96,color:#fff + style Partners fill:#5a3a1a,stroke:#7d5c3c,color:#fff + style Classic fill:#4a4a4a,stroke:#666,color:#fff + style LangGraph fill:#3d3d5c,stroke:#555,color:#fff +``` + +Users typically import from `langchain` (the actively maintained package) to access agents and high-level orchestration. The `langchain-core` layer is available for direct use when building custom implementations. Partner packages are loaded on-demand (often implicitly via `init_chat_model`) and are released independently from the core. `langchain-classic` (legacy) is maintained for backward compatibility but should not be used in new projects. + +## Three-Layer Architecture + +### Layer 1: langchain-core (Stable Base Abstractions) + +**Owns**: Base classes and protocols that define the contract for all LangChain ecosystem implementations. + +**Key responsibilities**: + +- **Runnable Protocol**: The foundational abstraction for all composable units. `Runnable[Input, Output]` defines `invoke()`, `stream()`, `batch()`, and async variants. All language models, tools, chains, and transformers implement this interface. + +- **BaseChatModel & LanguageModelInput**: Abstract base for chat models. All provider implementations (ChatOpenAI, ChatAnthropic, etc.) extend this class. Handles message encoding, token streaming, structured output marshaling, and token counting. + +- **Messages and Message Types**: The canonical message representation (AIMessage, ToolMessage, UserMessage, SystemMessage, etc.). Enables a unified protocol for model interaction regardless of provider. + +- **Tools (BaseTool)**: Abstraction for executable tools. Supports sync/async invocation, schema generation, and structured argument parsing. + +- **Prompts, Output Parsers, and Retrievers**: Base abstractions for prompt templates, structured output parsing, and document retrieval—all are Runnables. + +- **Callbacks and Tracing**: Callback manager infrastructure for instrumentation, logging, and integration with LangSmith. + +**Stability guarantee**: langchain-core follows a strict semantic versioning policy with advance notice of breaking changes. Core abstractions are stable across major versions. + +**Location**: `/libs/core/langchain_core/` + +### Layer 2: langchain (Agent Orchestration and High-Level APIs) + +**Owns**: The Agent Factory, agent middleware system, high-level chat model factory, and LangGraph-based agent execution orchestration. + +**Key responsibilities**: + +- **Agent Factory (`create_agent()`)**: Constructs a compiled LangGraph state machine that orchestrates the agentic loop. Handles model invocation, tool binding, structured output parsing, and middleware composition. Returns a runnable that accepts messages and yields model responses and tool calls. + +- **Agent Middleware System**: Pluggable hooks (`wrap_model_call`, `wrap_tool_call`) for injecting logic at model, tool, and lifecycle boundaries. Middleware composes vertically and can modify request state, rewrite tools dynamically, intercept model responses, and control loop flow. + +- **Init Chat Model (`init_chat_model()`)**: Factory function that dynamically loads and instantiates chat models by provider name and model identifier (e.g., `"openai:gpt-4o"`). Handles provider discovery, dependency management, and configuration injection. + +- **Structured Output and Response Formatting**: Abstractions for specifying desired output formats (JSON schemas, Pydantic models, tools) and marshaling model responses into typed Python objects. + +- **Agent State Management**: The `AgentState` schema, message accumulation with reducers, and ephemeral control fields (e.g., `jump_to` for middleware-driven routing). + +**Dependencies**: +- Requires langchain-core for abstractions (Runnable, BaseChatModel, tools, messages) +- Requires LangGraph for state management and graph compilation +- Partner packages loaded on-demand via init_chat_model + +**Location**: `/libs/langchain_v1/langchain/agents/`, `/libs/langchain_v1/langchain/chat_models/` + +### Layer 3: Partner Integrations (Provider-Specific Implementations) + +**Owns**: Each partner package implements core abstractions for a specific model provider or service. + +**Common structure**: + +- **Chat Models** (e.g., `ChatOpenAI`): Extend `BaseChatModel`, wrap provider API, handle authentication, token counting, streaming, and cost tracking. +- **Embeddings** (e.g., `OpenAIEmbeddings`): Implement embedding model interface. +- **Tools**: Provider-specific tool wrappers and utilities. +- **Structured Output Support**: Provider-specific strategies for enforcing output schemas (e.g., function calling, JSON mode). + +**Examples**: langchain-openai, langchain-anthropic, langchain-ollama, langchain-groq, langchain-mistralai + +**Release policy**: Partner packages are versioned independently. A partner package update does not require updates to langchain or langchain-core, and vice versa. Each partner manages its own API version pinning and compatibility. + +**Location**: `/libs/partners//langchain_/` + +--- + +## Component Interactions + +### Chat Model Resolution and Instantiation + +The `init_chat_model()` function provides the primary user-facing entry point for chat models: + + +```text +sequenceDiagram + participant User + participant InitCM as init_chat_model() + participant Registry as Provider Registry + participant Partner as Partner Package + participant Model as ChatOpenAI + + User->>InitCM: init_chat_model(identifier="openai:gpt-4o",
api_key=...) + InitCM->>InitCM: Parse identifier to provider, model_name + InitCM->>Registry: Lookup provider config + Registry-->>InitCM: (module, class, factory_fn) + InitCM->>Partner: Import langchain_openai + Partner-->>InitCM: ChatOpenAI class + InitCM->>Model: factory_fn(ChatOpenAI, model="gpt-4o",
api_key=...) + Model-->>InitCM: Initialized model instance + InitCM-->>User: BaseChatModel (ChatOpenAI) +``` + +The resolution process is lazy: `init_chat_model()` only imports the partner package when the user requests that provider, avoiding hard dependencies. + +### Agent Creation and Graph Construction + +When `create_agent()` is called, the factory builds a LangGraph state machine: + + +```text +sequenceDiagram + participant User + participant Factory as Agent Factory + participant StateGraph as LangGraph
StateGraph + participant Middleware as Middleware Stack + participant Graph as Compiled Graph + + User->>Factory: create_agent(model, tools, middleware=[...]) + Factory->>Factory: Merge middleware state schemas + Factory->>StateGraph: new StateGraph(merged_AgentState) + Factory->>StateGraph: add_node("model", model_node) + Factory->>StateGraph: add_node("tools", tool_node) + Factory->>StateGraph: add_edge(START, entry_node) + + Factory->>Middleware: Compose wrap_model_call layers + Factory->>Middleware: Compose wrap_tool_call layers + + Factory->>StateGraph: set_entry_point(entry_node) + Factory->>StateGraph: add_conditional_edges(after_model_node,
route_to_tools_or_exit) + + Factory->>Graph: compile() + Graph-->>Factory: CompiledStateGraph + Factory-->>User: Runnable agent +``` + +The compiled graph is a `Runnable[InputAgentState, OutputAgentState]`. Users invoke it with a list of messages; the agent orchestrates the model-tool loop internally. + +### Agent Execution Loop + +Once compiled and invoked, the agent follows this sequence: + +```mermaid +stateDiagram-v2 + [*] --> BeforeAgent: User calls agent.invoke(messages=[...]) + + BeforeAgent: Run before_agent middleware hooks + BeforeAgent --> BeforeModel: State updated + + BeforeModel: Run before_model middleware hooks + BeforeModel --> ModelCall: State updated or jump_to set + + ModelCall: Call language model
with current messages + ModelCall --> AfterModel: Receive AIMessage + + AfterModel: Run after_model middleware hooks + AfterModel --> Decision: Inspect jump_to or tool_calls + + Decision --> ToolExec: Has tool calls and not jumped + Decision --> Exit: No tool calls or jump_to=end + Decision --> LoopBack: jump_to=model (reloop) + + ToolExec: Execute tools in parallel
Wrap results in ToolMessages + ToolExec --> BeforeModel: Add ToolMessages to state + + LoopBack --> BeforeModel + + Exit: Run after_agent middleware hooks + Exit --> [*]: Return OutputAgentState +``` + +The `jump_to` field enables middleware to override routing (e.g., exit early, restart the model, skip tools). The `messages` field accumulates all messages (user, assistant, tool results) using the `add_messages` reducer, providing full conversation history to each model invocation. + +--- + +## Key Architectural Patterns + +### Runnable Composition + +All composable units (models, chains, tools, prompt templates) implement the `Runnable` protocol. This enables seamless composition: + +```python +# langchain-core defines the pattern +chain = prompt | model | output_parser + +# Works regardless of provider +model = init_chat_model("openai:gpt-4o") # ChatOpenAI +model = init_chat_model("anthropic:claude-3") # ChatAnthropic +``` + +Providers implement `BaseChatModel` (a Runnable), and the composition works identically. + +### Middleware as Composable Hooks + +The Agent Factory supports multiple middleware layers, each implementing one or more hooks: + +- `wrap_model_call(request, handler)`: Intercept and modify model requests, rewrite tools, post-process responses, or implement retry logic. +- `wrap_tool_call(request, handler)`: Intercept tool invocations, implement custom execution, or handle dynamic tools. +- Lifecycle hooks: `before_agent`, `before_model`, `after_model`, `after_agent`. + +Middleware is composed as a stack (inner → outer), enabling concerns like observability, safety, or logging to be added orthogonally. + +### Provider Abstraction + +Partners implement `BaseChatModel` but are free to extend it with provider-specific features. The core interface remains stable: + +```python +class BaseChatModel(Runnable[LanguageModelInput, AIMessage]): + def invoke(self, input: LanguageModelInput) -> AIMessage: ... + async def ainvoke(self, ...) -> AIMessage: ... + def stream(self, input: LanguageModelInput) -> Iterator[AIMessageChunk]: ... +``` + +Provider-specific structured output, cost tracking, and streaming options are layered on top without breaking the core contract. This allows users to swap models with minimal code changes. + +### Stable Core, Fluid Orchestration + +The core layer (langchain-core) is intentionally minimal and stable. Orchestration logic, middleware, and high-level patterns live in the langchain layer, which can evolve more rapidly. Partners remain independent, allowing rapid integration of new providers without coordinating core or langchain releases. + +--- + +## Versioning and Release Policy + +- **langchain-core** (`v1.6.1`): Stable base abstractions. Major version bumps are rare and announced in advance. Deprecations carry multiple minor versions of notice. This is the "least-moving" part of the ecosystem. + +- **langchain** (`v1.4.0`): Main user-facing package. Minor versions may add new agent patterns, middleware types, or orchestration improvements. Patch versions fix bugs. Requires specific langchain-core version (e.g., `>=1.6.0,<2.0.0`). + +- **langchain-classic** (`v1.0.8`): Legacy package for backward compatibility. Provides old chains, `langchain-community` re-exports, and deprecated APIs. New projects should use `langchain` instead. + +- **Partner packages**: Independent versioning. langchain-openai, langchain-anthropic, etc., release on their own schedules. Partners declare dependencies on langchain-core (required) and optionally langchain (optional, only if they provide middleware or agent-specific features). + +--- + +## Key Files and Symbols + +### langchain-core + +- `Runnable[Input, Output]` (`/libs/core/langchain_core/runnables/base.py`): The foundational protocol for all composable units. Defines `invoke()`, `stream()`, `batch()`, and async variants. + +- `BaseChatModel` (`/libs/core/langchain_core/language_models/chat_models.py`): Abstract base for all chat models. Providers extend this class. + +- `BaseTool` (`/libs/core/langchain_core/tools/`): Abstract base for tools. Enables schema generation, structured argument parsing, and sync/async execution. + +- Messages (`/libs/core/langchain_core/messages/`): `AIMessage`, `ToolMessage`, `UserMessage`, `SystemMessage`, etc. Form the canonical message representation. + +### langchain + +- `create_agent()` (`/libs/langchain_v1/langchain/agents/factory.py`): Constructs the agent graph. Accepts model, tools, middleware, and returns a compiled Runnable. + +- `init_chat_model()` (`/libs/langchain_v1/langchain/chat_models/base.py`): Factory function for dynamically loading chat models by provider identifier. + +- `AgentMiddleware` (`/libs/langchain_v1/langchain/agents/middleware/types.py`): Base class for middleware. Users subclass this to implement custom hooks. + +- `AgentState` (`/libs/langchain_v1/langchain/agents/middleware/types.py`): TypedDict defining the agent's state schema. Extensible via middleware `state_schema` attribute. + +### Partners + +- `ChatOpenAI` (`/libs/partners/openai/langchain_openai/chat_models/base.py`): Extends BaseChatModel, wraps the OpenAI API, handles streaming and structured output. + +- Similar implementations exist for Anthropic, Groq, Ollama, Mistral, and other providers. + +--- + +## Extension Points + +### Implementing a Custom Model Provider + +To add a new provider (e.g., a private LLM service): + +1. Create a new package: `langchain_myprovider/` +2. Extend `BaseChatModel` with your API client +3. Implement required methods: `_generate()` (or `_stream()` for streaming support), `_llm_type`, `model_parameters` +4. Optionally add middleware for provider-specific features +5. Register in `init_chat_model()` by PR to langchain (or publish independently and users can instantiate directly) + +### Implementing Middleware + +To add cross-cutting concerns (logging, rate-limiting, validation): + +1. Extend `AgentMiddleware` +2. Implement one or more hooks: `wrap_model_call()`, `wrap_tool_call()`, `before_agent()`, `after_agent()`, etc. +3. Optionally declare a `state_schema` to extend the agent's state +4. Pass to `create_agent(middleware=[...])` + +Middleware stacks vertically; each layer can wrap the next, enabling composition of unrelated concerns. + +### Custom Tools + +Tools are Runnables and can be defined as Python functions annotated with `@tool` or by extending `BaseTool`: + +```python +from langchain_core.tools import BaseTool + +class MyTool(BaseTool): + name = "my_tool" + description = "Does something useful" + + def _run(self, arg: str) -> str: + return f"Result for {arg}" +``` + +Tools are bound to agents at creation time and made available to the model for invocation. + +--- + +## Dependency Summary + +| Package | Depends On | Role | +|---------|-----------|------| +| **langchain-core** | langsmith, httpx, pydantic | Base abstractions; stable | +| **langchain** | langchain-core, langgraph, pydantic | Agent orchestration; user-facing | +| **langchain-classic** | langchain-core, langchain-text-splitters, pydantic | Legacy chains and community re-exports | +| **langchain-openai** | langchain-core, openai SDK | OpenAI integration (ChatOpenAI, embeddings) | +| **langchain-anthropic** | langchain-core, anthropic SDK | Anthropic integration (ChatAnthropic) | +| **langchain-ollama** | langchain-core, ollama SDK | Ollama integration (ChatOllama) | +| **langchain-groq** | langchain-core, groq SDK | Groq integration (ChatGroq) | + +Partners only depend on langchain-core (the abstractions), not langchain (the orchestration), enabling independent release cycles. diff --git a/openwiki/callbacks.md b/openwiki/callbacks.md new file mode 100644 index 0000000000..c4c2fe6f49 --- /dev/null +++ b/openwiki/callbacks.md @@ -0,0 +1,468 @@ +--- +type: "Reference" +title: "> Entering new SequentialChain chain..." +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-c9313cf42f0120d86b20245f + resource: repo://libs/core/langchain_core/callbacks/base.py + - id: openwiki-source-b15ceb5ed590ce6a2b569ed6 + resource: repo://libs/core/langchain_core/callbacks/file.py + - id: openwiki-source-1c233fccf5a66b84d0045366 + resource: repo://libs/core/langchain_core/callbacks/manager.py + - id: openwiki-source-55497dabc655f803e40dc13a + resource: repo://libs/core/langchain_core/callbacks/stdout.py + - id: openwiki-source-c7a2c3ef4ec61c3e28011205 + resource: repo://libs/core/langchain_core/callbacks/streaming_stdout.py + - id: openwiki-source-2685400b7962e4c90cefe9df + resource: repo://libs/core/langchain_core/callbacks/usage.py + - id: openwiki-source-079792f059657900794e2955 + resource: repo://libs/core/langchain_core/runnables/config.py + - id: openwiki-source-bfd8b1aa6ad00852a2e99762 + resource: repo://libs/core/langchain_core/tracers/context.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +LangChain's callback system provides a unified mechanism for observability, logging, and tracing of all operations—LLM calls, chain execution, tool usage, and retrieval. Callbacks allow developers to monitor execution flow, collect metrics, stream output, and integrate with external observability platforms like LangSmith without modifying core application code. + +The system is built on a hierarchical run structure where parent-child relationships are tracked through run IDs, enabling comprehensive trace trees. Each operation generates events that are dispatched to one or more registered handlers, which can act on them synchronously or asynchronously. + +## Architecture + +### Core Components + +**BaseCallbackHandler** (`repo://libs/core/langchain_core/callbacks/base.py#L496-L546`) is the base class for all callback implementations. It inherits from multiple mixins that define event methods for different operation types: + +- **LLMManagerMixin**: `on_llm_start`, `on_llm_new_token`, `on_llm_end`, `on_llm_error`, `on_stream_event` +- **ChainManagerMixin**: `on_chain_start`, `on_chain_end`, `on_chain_error` +- **ToolManagerMixin**: `on_tool_start`, `on_tool_end`, `on_tool_error` +- **RetrieverManagerMixin**: `on_retriever_start`, `on_retriever_end`, `on_retriever_error` +- **AgentManagerMixin**: `on_agent_action`, `on_agent_finish` +- **RunManagerMixin**: `on_text`, `on_retry`, `on_custom_event` +- **CallbackManagerMixin**: start methods for all operation types + +Every handler also supports `raise_error` and `run_inline` attributes to control error propagation and execution context. + +**BaseCallbackManager** (`repo://libs/core/langchain_core/callbacks/base.py#L1004-L1227`) manages a collection of handlers and their lifecycle. It maintains: + +- **handlers**: non-inheritable callbacks for the current operation +- **inheritable_handlers**: callbacks passed down to child operations +- **tags**: labels for filtering and organizing runs (inheritable) +- **metadata**: JSON-serializable context (inheritable) +- **parent_run_id**: reference to parent operation for hierarchy + +**CallbackManager** (sync, `repo://libs/core/langchain_core/callbacks/manager.py#L1377-L1726`) and **AsyncCallbackManager** (async, `repo://libs/core/langchain_core/callbacks/manager.py#L1859`) are the primary implementations that dispatch events to handlers. They provide `on_llm_start`, `on_chat_model_start`, `on_chain_start`, `on_tool_start`, and `on_retriever_start` methods that return specialized run managers bound to a specific operation. + +**Run Managers** are returned from start events and provide context-bound methods for end and error events: + +- **CallbackManagerForLLMRun**: `on_llm_new_token`, `on_llm_end`, `on_llm_error`, `on_stream_event` +- **CallbackManagerForChainRun**: `on_chain_end`, `on_chain_error`, `on_agent_action`, `on_agent_finish` +- **CallbackManagerForToolRun**: `on_tool_end`, `on_tool_error` +- **CallbackManagerForRetrieverRun**: `on_retriever_end`, `on_retriever_error` + +Each run manager holds the **run_id** and **parent_run_id** for the operation, ensuring trace hierarchy is preserved. + +### Event Dispatch + +The callback system uses two dispatch functions: + +**handle_event** (`repo://libs/core/langchain_core/callbacks/manager.py#L285-L368`, sync) iterates over handlers and calls the corresponding event method. It handles: + +- Skipping handlers based on ignore conditions (e.g., `ignore_llm`, `ignore_chain`) +- Catching and logging exceptions (respecting `raise_error`) +- Collecting async coroutines and running them via executor pool or event loop +- Converting `on_chat_model_start` to `on_llm_start` fallback when not implemented + +**ahandle_event** (`repo://libs/core/langchain_core/callbacks/manager.py#L453-L488`, async) separates inline (sequential) and non-inline (concurrent) handlers, using `asyncio.gather()` for parallelism. + +The **shielded** decorator (`repo://libs/core/langchain_core/callbacks/manager.py#L221-L254`) preserves context variables in async handlers when cancellation occurs, avoiding task cancellation deadlocks. + +## Built-in Handlers + +### StdOutCallbackHandler + +**Location**: `repo://libs/core/langchain_core/callbacks/stdout.py` + +Prints human-readable messages to standard output for chain entry/exit, agent actions, tool observations, and freeform text. Useful for interactive CLI applications. + +```python +from langchain_core.callbacks.stdout import StdOutCallbackHandler + +handler = StdOutCallbackHandler(color=None) +chain.invoke(input, config={"callbacks": [handler]}) +# > Entering new SequentialChain chain... +# > Finished chain. +``` + +Methods override: +- `on_chain_start`: Prints "Entering new {name} chain" +- `on_chain_end`: Prints "Finished chain" +- `on_agent_action`: Prints action log +- `on_tool_end`: Prints tool observation with optional prefixes +- `on_text`: Prints arbitrary text + +### FileCallbackHandler + +**Location**: `repo://libs/core/langchain_core/callbacks/file.py` + +Writes callback events to a file with optional coloring. Supports both context manager (recommended) and direct instantiation patterns. + +```python +from langchain_core.callbacks.file import FileCallbackHandler + +# Context manager (recommended) +with FileCallbackHandler("output.txt") as handler: + chain.invoke(input, config={"callbacks": [handler]}) + +# Direct instantiation (deprecated) +handler = FileCallbackHandler("output.txt") +try: + chain.invoke(input, config={"callbacks": [handler]}) +finally: + handler.close() +``` + +The handler opens a file in append mode by default and writes formatted output for chains, agents, and tools. + +### StreamingStdOutCallbackHandler + +**Location**: `repo://libs/core/langchain_core/callbacks/streaming_stdout.py` + +Streams individual LLM tokens to stdout as they are generated during streaming, enabling real-time output visualization. + +```python +from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler + +handler = StreamingStdOutCallbackHandler() +llm.stream("Hello", config={"callbacks": [handler]}) # Prints tokens as they arrive +``` + +Only works with LLMs that support streaming via `on_llm_new_token`. + +### UsageMetadataCallbackHandler + +**Location**: `repo://libs/core/langchain_core/callbacks/usage.py` + +Aggregates token usage metadata across LLM calls, collecting input/output tokens and cost information keyed by model name. Thread-safe using internal locks. + +```python +from langchain_core.callbacks.usage import UsageMetadataCallbackHandler + +callback = UsageMetadataCallbackHandler() +llm_1.invoke("Hello", config={"callbacks": [callback]}) +llm_2.invoke("Hello", config={"callbacks": [callback]}) +print(callback.usage_metadata) +# {"openai:gpt-4": UsageMetadata(...), "anthropic:claude-3": UsageMetadata(...)} +``` + +## LangSmith Integration + +LangSmith is the observability platform for LangChain. When enabled via environment variables or context managers, all callback events are automatically sent to LangSmith for visualization and analysis. + +### Automatic Tracing + +**Enable with environment variables**: +```bash +export LANGCHAIN_TRACING_V2=true +export LANGSMITH_API_KEY= +export LANGSMITH_PROJECT= +``` + +When `LANGCHAIN_TRACING_V2` is enabled, the callback manager automatically creates a **LangChainTracer** (`repo://libs/core/langchain_core/tracers/langchain.py`) and registers it as a handler. This tracer: + +- Captures all run lifecycle events (start, end, error) +- Builds a hierarchical trace tree using parent_run_id +- Sends traces to the LangSmith backend +- Provides run URLs and unique run IDs + +**Context manager approach**: + +```python +from langchain_core.tracers.context import tracing_v2_enabled + +with tracing_v2_enabled(project_name="my_project") as tracer: + chain.invoke("hello") + run_url = tracer.get_run_url() + print(f"View trace at: {run_url}") +``` + +**Automatic trace callback injection** (`repo://libs/core/langchain_core/tracers/context.py#L105-L130`) happens via `_get_trace_callbacks`, which: + +1. Checks if tracing is enabled via `_tracing_v2_is_enabled()` +2. Creates or reuses an existing LangChainTracer +3. Adds it to the callback manager without duplication +4. Returns the configured callback manager for use in operations + +### Run Collection + +For programmatic access to trace data without LangSmith: + +```python +from langchain_core.tracers.context import collect_runs + +with collect_runs() as runs_cb: + chain.invoke("hello") + for run in runs_cb.traced_runs: + print(f"Run ID: {run.id}, Type: {run.run_type}") +``` + +The **RunCollectorCallbackHandler** gathers all Run objects in a list, enabling offline analysis. + +## Configuration and Registration + +### Via RunnableConfig + +Runnables accept callbacks through the `config` parameter: + +```python +from langchain_core.runnables.config import RunnableConfig + +config = RunnableConfig( + callbacks=[handler1, handler2], + tags=["production", "v1"], + metadata={"user_id": "123", "session": "abc"}, + run_name="my_run", +) +chain.invoke(input, config=config) +``` + +The **RunnableConfig** TypedDict (`repo://libs/core/langchain_core/runnables/config.py#L57-L129`) supports: + +- **callbacks**: Handler list or callback manager +- **tags**: Inheritable labels for filtering +- **metadata**: Inheritable context (JSON-serializable) +- **run_name**: Override default operation name +- **run_id**: Explicit run identifier (UUID) + +### Manager Configuration + +The static `configure` method creates a fully initialized callback manager: + +```python +from langchain_core.callbacks.manager import CallbackManager + +manager = CallbackManager.configure( + inheritable_callbacks=[tracer], + local_callbacks=[stdout_handler], + inheritable_tags=["app"], + local_tags=["expensive_op"], + inheritable_metadata={"env": "prod"}, + verbose=True, +) +chain.invoke(input, config={"callbacks": manager}) +``` + +### Handler Management + +The callback manager API for dynamic handler registration: + +```python +manager = CallbackManager(handlers=[]) +manager.add_handler(handler, inherit=True) # Add to both handler lists +manager.add_handler(handler, inherit=False) # Add only to current operation +manager.remove_handler(handler) +manager.set_handlers([h1, h2], inherit=True) # Replace all handlers +``` + +### Tag and Metadata Management + +```python +manager.add_tags(["tag1", "tag2"], inherit=True) +manager.remove_tags(["tag1"]) + +manager.add_metadata({"key": "value"}, inherit=True) +manager.remove_metadata(["key"]) +``` + +Tags and metadata are passed to all callbacks via keyword arguments in event methods. + +## Callback Lifecycle and Execution + +### Run Hierarchy + +Operations form a parent-child hierarchy where each child receives a **parent_run_id** linking it to the parent: + +``` +LLMResult -> parent_run_id=run_id_chain -> parent_run_id=run_id_outer +``` + +This enables LangSmith to reconstruct the full call tree. The **ParentRunManager** (`repo://libs/core/langchain_core/callbacks/manager.py#L599-L619`) provides `get_child()` to create child callback managers that inherit tags and inheritable handlers. + +### Inheritance Rules + +- **inheritable_handlers**: Passed to all child operations +- **handlers**: Only used for current operation (not passed to children) +- **inheritable_tags**: Passed to children, accumulated +- **inheritable_metadata**: Passed to children, merged + +This allows global handlers (e.g., LangSmith tracer) while supporting operation-specific ones. + +### Async Execution Control + +The `run_inline` attribute on a handler determines execution: + +- **run_inline=True**: Execute in the current async context (sequential) +- **run_inline=False**: Schedule on thread pool or concurrent tasks + +```python +class MyInlineHandler(BaseCallbackHandler): + run_inline = True # Always runs in caller's context + +class MyConcurrentHandler(BaseCallbackHandler): + run_inline = False # Runs in executor or concurrently +``` + +Inline handlers respect the caller's execution context (ContextVar), while non-inline handlers use `copy_context().run()` to preserve context variables across thread boundaries. + +## Thread Safety and Context Variables + +### Context Preservation + +The callback system uses Python's `contextvars` module to propagate context across sync/async boundaries: + +- **tracing_v2_callback_var**: Current LangChainTracer (if tracing enabled) +- **run_collector_var**: Current RunCollectorCallbackHandler (if collecting) +- **var_child_runnable_config**: Child runnable config from parent + +When `handle_event` runs async coroutines from sync context, it preserves context variables via `copy_context().run()`. + +### Thread Safety + +Handlers should implement thread-safe state management if accessed by multiple handlers concurrently. The **UsageMetadataCallbackHandler** uses `threading.Lock()` for aggregation. + +## Streaming and Token Events + +### LLM Token Streaming + +When an LLM supports streaming, the callback manager calls `on_llm_new_token` for each token: + +```python +class TokenCollector(BaseCallbackHandler): + def __init__(self): + self.tokens = [] + + def on_llm_new_token(self, token, **kwargs): + self.tokens.append(token) + +collector = TokenCollector() +llm.stream("Hello", config={"callbacks": [collector]}) +print("".join(collector.tokens)) +``` + +The **chunk** parameter provides the full `GenerationChunk` or `ChatGenerationChunk` object with additional metadata. + +### Protocol Events (v3) + +For native streaming providers using the v3 protocol: + +```python +class ProtocolEventHandler(BaseCallbackHandler): + def on_stream_event(self, event, **kwargs): + # event is MessagesData: message-start, content-block-start, etc. + print(f"Event type: {event['type']}") +``` + +Protocol events fire at finer granularity than `on_llm_new_token`, with explicit lifecycle boundaries. + +## Error Handling + +### Exception Propagation + +By default, handler exceptions are logged and swallowed: + +```python +class BuggyHandler(BaseCallbackHandler): + def on_chain_start(self, **kwargs): + raise ValueError("Oops!") # Logged but doesn't stop execution + +handler = BuggyHandler() +chain.invoke(input, config={"callbacks": [handler]}) # Still runs +``` + +To enforce strict error checking: + +```python +handler = BuggyHandler() +handler.raise_error = True +chain.invoke(input, config={"callbacks": [handler]}) # Raises ValueError +``` + +### Ignore Conditions + +Handlers can opt out of specific event types: + +```python +class LLMOnlyHandler(BaseCallbackHandler): + @property + def ignore_chain(self): + return True # Skip all chain events + + @property + def ignore_retriever(self): + return True # Skip all retriever events +``` + +Available properties: `ignore_llm`, `ignore_chain`, `ignore_agent`, `ignore_tool`, `ignore_retriever`, `ignore_retry`, `ignore_chat_model`, `ignore_custom_event`. + +## Custom Event Dispatch + +### on_custom_event + +For application-specific events beyond LLM/chain/tool: + +```python +manager = CallbackManager(handlers=[custom_handler]) +manager.on_custom_event( + name="user_interaction", + data={"user_id": 123, "action": "clicked_button"}, +) +``` + +Handlers receive: + +```python +def on_custom_event(self, name, data, run_id, tags, metadata, **kwargs): + print(f"Event: {name}, Data: {data}") +``` + +## Chain Groups + +For grouping multiple separate calls as a single logical operation: + +```python +from langchain_core.callbacks.manager import trace_as_chain_group + +with trace_as_chain_group("data_processing", tags=["batch"]) as manager: + result1 = llm.invoke("query1", config={"callbacks": manager}) + result2 = chain.invoke(input2, config={"callbacks": manager}) + # Both treated as a single chain in LangSmith trace +``` + +The manager tracks completion state and calls parent's `on_chain_end` or `on_chain_error` when exiting. + +## Best Practices + +1. **Use context managers for file handlers**: FileCallbackHandler should be used with `with` statement to ensure proper cleanup. + +2. **Register global handlers via inheritable_callbacks**: Use `CallbackManager.configure(inheritable_callbacks=[...])` for handlers that should apply everywhere. + +3. **Enable LangSmith in production**: Set `LANGCHAIN_TRACING_V2=true` and `LANGSMITH_API_KEY` for automatic trace collection. + +4. **Use tags for filtering**: Add semantic tags ("production", "experiment", "expensive") to filter runs in LangSmith. + +5. **Include metadata context**: Embed user IDs, session IDs, and environment info in metadata for better observability. + +6. **Implement thread-safe handlers**: If your handler accesses shared state, use locks or thread-local storage. + +7. **Handle exceptions gracefully**: Set `raise_error=True` only for critical handlers; others should log and continue. + +8. **Respect ignore conditions**: If your handler only cares about LLM calls, set `ignore_chain=True` to skip irrelevant events. + +9. **Use streaming handlers for real-time feedback**: StreamingStdOutCallbackHandler enables interactive token-by-token output. + +10. **Collect token usage**: Use UsageMetadataCallbackHandler to track costs across multi-model applications. diff --git a/openwiki/chat-models.md b/openwiki/chat-models.md new file mode 100644 index 0000000000..63b537bda8 --- /dev/null +++ b/openwiki/chat-models.md @@ -0,0 +1,595 @@ +--- +type: "Architecture" +title: "Chat Model Interface and Lifecycle" +description: "Document BaseChatModel protocol, input/output handling, streaming, and integration points with callbacks and model profiling." +tags: [chat-models, llm-integration, streaming, structured-output, model-capabilities] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-132f3183693cd9cf79d029a5 + resource: repo://libs/core/langchain_core/language_models/base.py + - id: openwiki-source-5f8bc32563177d89fbab9b2f + resource: repo://libs/core/langchain_core/language_models/chat_model_stream.py + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-a0aef6917b7e1f4a06e6db95 + resource: repo://libs/core/langchain_core/language_models/model_profile.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +The **chat model system** is the core interface for integrating large language models into LangChain applications. `BaseChatModel` is the abstract protocol that all chat model implementations inherit from. It defines the contract for synchronous and asynchronous invoke/streaming behavior, callback integration, rate limiting, structured output binding, and capability discovery via model profiles. + +Chat models convert conversational message history into AI responses, supporting both simple generation (`invoke`) and streaming output (`stream`). The framework unifies sync/async patterns, handles caching transparently, routes to streaming or non-streaming backends based on configuration and attached callbacks, and provides extension points for custom behavior via method overrides. + +## Core Interface: BaseChatModel + +**Location**: `repo://libs/core/langchain_core/language_models/chat_models.py#L284-L2400` + +`BaseChatModel` inherits from `BaseLanguageModel[AIMessage]` and is a `Runnable` that accepts `LanguageModelInput` and produces `AIMessage` outputs. It is designed for subclassing; implementations must override `_generate` (required) and optionally `_llm_type`, `_stream`, and `_agenerate`. + +### Input and Output Types + +**LanguageModelInput** (`repo://libs/core/langchain_core/language_models/base.py#L140`) is a union type: + +```python +LanguageModelInput = PromptValue | str | Sequence[MessageLikeRepresentation] +``` + +- **string**: Converted to a `StringPromptValue` (simple user message) +- **list of messages**: Converted to a `ChatPromptValue` (full conversation history) +- **PromptValue**: Already a structured prompt (passed through) + +The `_convert_input` method normalizes all input forms to a `PromptValue` for downstream processing. + +**Output**: All invoke/stream methods return `AIMessage` or `AIMessageChunk` (for streaming). Chat results are wrapped in `ChatGeneration` objects (holding message + generation metadata) aggregated into `ChatResult`. + +### Synchronous Methods + +**`invoke`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L474-L499`) is the primary synchronous entrypoint: + +```python +def invoke( + self, + input: LanguageModelInput, + config: RunnableConfig | None = None, + *, + stop: list[str] | None = None, + **kwargs: Any, +) -> AIMessage +``` + +- Converts input to `PromptValue`, then to messages +- Calls `generate_prompt` (which internally calls `_generate_with_cache`) +- Extracts and returns the first generation's message +- Propagates `run_id`, callbacks, tags, and metadata from config + +**`stream`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L726-L856`) yields `AIMessageChunk` objects as they arrive: + +```python +def stream( + self, + input: LanguageModelInput, + config: RunnableConfig | None = None, + *, + stop: list[str] | None = None, + **kwargs: Any, +) -> Iterator[AIMessageChunk] +``` + +- Checks if streaming is enabled and implemented via `_should_stream()` +- Falls back to `invoke` if streaming is disabled or not implemented +- For streaming-enabled models, calls `_stream()` directly and yields chunks +- Wraps output in callback lifecycle: `on_chat_model_start`, `on_llm_new_token` (per chunk), `on_llm_end` or `on_llm_error` +- Applies rate limiting if configured +- Normalizes messages and handles streaming-specific output formatting (e.g., `output_version="v1"`) +- Yields a final empty chunk with `chunk_position="last"` when streaming completes + +### Asynchronous Methods + +**`ainvoke`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L501-L523`) is the async variant: + +```python +async def ainvoke( + self, + input: LanguageModelInput, + config: RunnableConfig | None = None, + *, + stop: list[str] | None = None, + **kwargs: Any, +) -> AIMessage +``` + +- Awaits `agenerate_prompt` +- Otherwise mirrors `invoke` behavior + +**`astream`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L857-L990`) is the async streaming variant: + +```python +async def astream( + self, + input: LanguageModelInput, + config: RunnableConfig | None = None, + *, + stop: list[str] | None = None, + **kwargs: Any, +) -> AsyncIterator[AIMessageChunk] +``` + +- Checks `_should_stream(async_api=True)` to route to `_astream` or fallback +- Otherwise mirrors `stream` behavior with async callback dispatch + +## Streaming Architecture + +### Stream Decision Logic + +**`_should_stream()`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L549-L585`) determines whether to use the streaming code path: + +```python +def _should_stream( + self, + *, + async_api: bool, + run_manager: CallbackManagerForLLMRun | AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> bool +``` + +Returns `True` if: +1. Streaming is not disabled (`_streaming_disabled()` returns `False`) +2. Streaming method is implemented for the requested variant (sync/async) +3. Any of these are true: + - Explicit `stream=True` kwarg + - Instance-level `streaming=True` attribute + - A v1-style `_StreamingCallbackHandler` is attached + +Returns `False` (fallback to non-streaming) if: +- `disable_streaming=True` (hard disable) +- `disable_streaming="tool_calling"` and tools are provided +- `stream=False` explicitly +- Streaming is not implemented and async falls back to sync + +### Stream Implementation Methods + +**`_stream()`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L2255-L2273`) is the sync streaming hook (optional override): + +```python +def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> Iterator[ChatGenerationChunk] +``` + +- Subclasses override to implement native streaming +- Default raises `NotImplementedError` (fallback to `_generate`) +- Receives run_manager for per-token callbacks + +**`_astream()`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L2275-L2311`) is the async streaming hook (optional override): + +```python +async def _astream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> AsyncIterator[ChatGenerationChunk] +``` + +- Default implementation runs `_stream()` in an executor and yields results +- Subclasses can override for native async streaming + +### ChatModelStream and AsyncChatModelStream + +**Location**: `repo://libs/core/langchain_core/language_models/chat_model_stream.py` + +For the v3 event protocol (`stream_events(version="v3")`), models return a `ChatModelStream` (sync) or `AsyncChatModelStream` (async) that expose **typed projections** for incremental content: + +- **`.text`**: Accumulates text content blocks +- **`.reasoning`**: Accumulates reasoning/chain-of-thought content +- **`.tool_calls`**: Accumulates parsed tool call blocks +- **`.usage`**: Accumulates token usage info +- **`.output`**: Final assembled `AIMessage` + +Each projection can be iterated for deltas or awaited for the final value. Internally, these accumulators track incoming protocol events and merge them into structured output. + +## Generation and Caching + +### Core Generation Methods + +**`_generate()`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L2208-L2226`) is the **required abstract method** all subclasses must implement: + +```python +@abstractmethod +def _generate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> ChatResult +``` + +- Calls the underlying model's API +- Returns a `ChatResult` with a list of `ChatGeneration` objects +- Must handle errors internally or propagate them +- Receives normalized messages and a run manager for callbacks + +**`_agenerate()`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L2228-L2253`) is the optional async override: + +```python +async def _agenerate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> ChatResult +``` + +- Default implementation runs `_generate` in an executor +- Subclasses override for native async API support + +### Cached Generation + +**`_generate_with_cache()`** and **`_agenerate_with_cache()`** wrap the core methods with: + +1. **Prompt caching**: Checks if `self.cache` or global `get_llm_cache()` has cached results for the input +2. **Cache hits**: Returns cached generations and replays them as v2 events if a v2 handler is attached +3. **Cache misses**: Routes through streaming or non-streaming path +4. **Protocol routing**: Dispatches to v2 events (`_should_use_protocol_streaming`) or v1 callback path (`_should_stream`) + +### Batch Methods + +**`generate()`** and **`agenerate()`** accept a list of message lists and use internal caching/streaming to batch-process prompts: + +```python +def generate( + self, + messages: list[list[BaseMessage]], + stop: list[str] | None = None, + callbacks: Callbacks = None, + **kwargs: Any, +) -> LLMResult +``` + +Returns an `LLMResult` with generations grouped by input prompt and combined llm_output. + +## Callback Lifecycle + +Chat models integrate with the callback system to emit structured events throughout execution: + +### LLM Run Lifecycle + +1. **`on_chat_model_start`** (or fallback `on_llm_start`): + - Fires when `invoke`, `stream`, or `generate` begins + - Receives serialized model config, formatted input messages, invocation params, and batch size + - Returns run manager(s) bound to the operation + +2. **`on_llm_new_token`** (streaming only): + - Fires once per streamed token/chunk + - Receives token string and `ChatGenerationChunk` metadata + - Allows real-time output capture + +3. **`on_llm_end`**: + - Fires when generation completes successfully + - Receives final `LLMResult` with all generations and metadata + +4. **`on_llm_error`**: + - Fires if generation raises an exception + - Receives the exception and partial `LLMResult` (if available) + - `_generate_response_from_error()` extracts response metadata from HTTP errors + +5. **`on_stream_event`** (v2/v3 protocol): + - Fires for each content-block protocol event during streaming + - Allows fine-grained event observation for advanced tracing + +### Callback Configuration + +Callbacks are configured via `RunnableConfig`: + +```python +config = { + "callbacks": [my_handler], # Callbacks for this run + "tags": ["agent", "tools"], # Labels for filtering + "metadata": {"user_id": "123"}, # Context data + "run_name": "my_run", # Human-readable run name + "run_id": uuid.uuid4(), # Explicit run ID (optional) +} +result = model.invoke(input, config=config) +``` + +Inheritable metadata and LangSmith params are extracted via `_get_invocation_params()` and `_get_ls_params()`. + +## Structured Output and Tool Binding + +### with_structured_output() + +**Location**: `repo://libs/core/langchain_core/language_models/chat_models.py#L2385-L2565` + +`with_structured_output()` wraps a chat model to constrain output to a specified schema: + +```python +def with_structured_output( + self, + schema: dict[str, Any] | type, + *, + include_raw: bool = False, + **kwargs: Any, +) -> Runnable[LanguageModelInput, dict[str, Any] | BaseModel] +``` + +**How it works**: + +1. Delegates to `bind_tools([schema], tool_choice="any", ...)` +2. Chains the result through an output parser: + - If schema is a Pydantic class: `PydanticToolsParser` → Pydantic instance + - If schema is a dict: `JsonOutputKeyToolsParser` → dict +3. If `include_raw=True`: Wraps output in `{"raw": AIMessage, "parsed": ..., "parsing_error": ...}` +4. If parsing fails and `include_raw=False`: Raises exception + +**Prerequisites**: Requires the model to implement `bind_tools()` (not all models support this). + +### bind_tools() + +**Location**: `repo://libs/core/langchain_core/language_models/chat_models.py#L2366-L2383` + +```python +def bind_tools( + self, + tools: Sequence[dict[str, Any] | type | Callable[..., Any] | BaseTool], + *, + tool_choice: str | None = None, + **kwargs: Any, +) -> Runnable[LanguageModelInput, AIMessage] +``` + +- Abstract method; must be implemented by subclasses that support tool calling +- Binds a list of tools to the model +- Returns a bound runnable that includes tool definitions in the API request +- `tool_choice="any"` forces the model to call at least one tool + +## Model Profiles and Capabilities + +**Location**: `repo://libs/core/langchain_core/language_models/model_profile.py` + +The `profile` field on `BaseChatModel` holds metadata about model capabilities: + +```python +class ModelProfile(TypedDict, total=False): + # Metadata + name: str # Human-readable model name + status: str # 'active', 'deprecated', etc. + release_date: str # ISO 8601 + last_updated: str # ISO 8601 + open_weights: bool # Weights publicly available? + + # Input constraints + max_input_tokens: int # Context window size + text_inputs: bool + image_inputs: bool + image_url_inputs: bool + pdf_inputs: bool + audio_inputs: bool + video_inputs: bool + image_tool_message: bool # Images in ToolMessage? + pdf_tool_message: bool # PDFs in ToolMessage? + + # Output constraints + max_output_tokens: int + text_outputs: bool + image_outputs: bool + audio_outputs: bool + video_outputs: bool + + # Capabilities + tool_calling: bool # Supports function calling? + tool_choice: bool # Supports tool_choice parameter? + tool_call_streaming: bool # Returns structured tool_call_chunks when streaming? + structured_output: bool # Native structured output support? + reasoning_output: bool # Reasoning/chain-of-thought? + reasoning_effort_levels: list[str] # ['low', 'medium', 'high'] + reasoning_effort_default: str + temperature: bool # Supports temperature parameter? + attachment: bool # Supports file attachments? +``` + +**Auto-loading**: Profiles are resolved via `_resolve_model_profile()` (subclass override) and cached in the `profile` field. Unrecognized keys trigger a warning via `_warn_unknown_profile_keys()`. + +### Partner Pattern Integration + +Partner packages (e.g., `langchain-openai`) override `_resolve_model_profile()` to load model-specific metadata from their own profile data. The base validator `_set_model_profile` (Pydantic mode="after") automatically populates the field if not explicitly set. + +## Configuration and State + +### Core Fields + +```python +class BaseChatModel(BaseLanguageModel[AIMessage], ABC): + rate_limiter: BaseRateLimiter | None = Field(default=None, exclude=True) + + disable_streaming: bool | Literal["tool_calling"] = False + # False: use streaming if available + # True: always use non-streaming (invoke) + # "tool_calling": use non-streaming only when tools are passed + + output_version: str | None = None + # 'v0': provider-specific format (lazy-parse via content_blocks) + # 'v1': standardized format (merged into content) + + profile: ModelProfile | None = Field(default=None, exclude=True) + # Capability metadata (auto-loaded if not provided) + + cache: BaseCache | None = None # Inherited from BaseLanguageModel + callbacks: list[BaseCallbackHandler] | None = None + verbose: bool = False + tags: list[str] | None = None + metadata: dict[str, Any] | None = None +``` + +### Required Properties + +- **`_llm_type`** (property, abstract): Unique model type identifier (e.g., `"openai"`, `"anthropic"`) +- **`_identifying_params`** (property, optional): Dict of model configuration for tracing (e.g., `{"model": "gpt-4", "temperature": 0.7}`) + +## Implementation Requirements + +Subclasses must implement: + +| Method/Property | Description | Required | Notes | +|---|---|---|---| +| `_generate()` | Core generation logic | ✓ | Calls provider API, returns `ChatResult` | +| `_llm_type` | Model type identifier | ✓ | String like `"openai"`, `"anthropic"` | +| `_identifying_params` | Config dict for tracing | ✗ | Used by `_get_llm_string()` and serialization | +| `_stream()` | Sync streaming | ✗ | Optional; if not implemented, stream falls back to invoke | +| `_agenerate()` | Native async generation | ✗ | Optional; defaults to running `_generate` in executor | +| `_astream()` | Native async streaming | ✗ | Optional; defaults to running `_stream` in executor | +| `bind_tools()` | Tool binding for structured output | ✗ | Required only if `with_structured_output()` is needed | + +## Model Initialization + +**Location**: `repo://libs/langchain_v1/langchain/chat_models/base.py` (v1 compat) and `langchain_core` partner packages + +Models are instantiated via: + +1. **Direct instantiation**: `ChatOpenAI(model="gpt-4", temperature=0)` +2. **Factory function `init_chat_model()`**: Auto-detects provider and imports the class dynamically +3. **Partner package exports**: Each provider (e.g., `langchain-openai`) exports a concrete model class + +The `init_chat_model()` function accepts a model name string (e.g., `"gpt-4"`, `"claude-3-sonnet"`) and optional `model_provider` to instantiate the correct class without explicit imports. + +## Example: Custom Chat Model + +```python +from langchain_core.language_models.chat_models import BaseChatModel +from langchain_core.messages import BaseMessage, AIMessage +from langchain_core.outputs import ChatResult, ChatGeneration +from langchain_core.callbacks import CallbackManagerForLLMRun + +class MyCustomChatModel(BaseChatModel): + """Custom chat model for demonstration.""" + + model_name: str = "my-model" + temperature: float = 0.7 + + def _generate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + """Generate a response from the messages.""" + # Call your model API here + response_text = f"Echo: {messages[-1].content}" + + message = AIMessage(content=response_text) + generation = ChatGeneration(message=message) + return ChatResult(generations=[generation]) + + def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + """Stream tokens from the model.""" + text = f"Echo: {messages[-1].content}" + for char in text: + chunk = ChatGenerationChunk( + message=AIMessageChunk(content=char) + ) + yield chunk + + @property + def _llm_type(self) -> str: + """Return the model type identifier.""" + return "my-custom-model" + + @property + def _identifying_params(self) -> dict[str, Any]: + """Return identifying parameters for tracing.""" + return { + "model_name": self.model_name, + "temperature": self.temperature, + } +``` + +## Advanced Patterns + +### Streaming with Callbacks + +```python +from langchain_core.callbacks import StreamingStdOutCallbackHandler + +handler = StreamingStdOutCallbackHandler() +config = {"callbacks": [handler]} + +# Streams token-by-token to stdout +for chunk in model.stream("Tell me a joke", config=config): + pass # Handler prints as chunks arrive +``` + +### Structured Output with Validation + +```python +from pydantic import BaseModel + +class Answer(BaseModel): + text: str + confidence: float + +structured_model = model.with_structured_output(Answer) +result = structured_model.invoke("What is 2+2?") # -> Answer(text="4", confidence=0.99) +``` + +### Caching and Rate Limiting + +```python +from langchain_core.caches import InMemoryCache +from langchain_core.rate_limiters import InMemoryRateLimiter + +model = ChatOpenAI( + model="gpt-4", + cache=InMemoryCache(), # Cache results + rate_limiter=InMemoryRateLimiter(requests_per_second=10) # Limit requests +) + +# Subsequent identical calls hit the cache +result1 = model.invoke("Hello") +result2 = model.invoke("Hello") # Cached, no API call +``` + +### Conditional Streaming + +```python +model_with_fallback = ChatOpenAI().with_fallbacks([ChatAnthropic()]) + +# Use streaming only when a handler requests it +config = {"callbacks": [MyStreamingHandler()]} +model_with_fallback.invoke("Prompt", config=config) +``` + +## Key Invariants and Guarantees + +1. **Input normalization**: All input forms (string, message list, PromptValue) are normalized to messages before `_generate`/`_stream` are called. + +2. **Message IDs**: Each streamed message chunk and final message gets a unique ID (derived from run_id) for tracing. + +3. **Callback ordering**: Callbacks fire in order: `on_chat_model_start` → `on_llm_new_token` (per chunk) → `on_llm_end` or `on_llm_error`. + +4. **Streaming fallback**: If streaming is not implemented or disabled, `stream` seamlessly falls back to `invoke` and yields the result as a single chunk. + +5. **Cache transparency**: Cache hits are completely transparent—same lifecycle callbacks fire as for cache misses. + +6. **Async/sync equivalence**: Async methods mirror sync behavior; default async implementations run sync methods in an executor. + +7. **Response metadata**: Each generation accumulates metadata (tokens, finish_reason, etc.) in `message.response_metadata`. + +8. **Error handling**: Exceptions during generation trigger `on_llm_error` and propagate to the caller; error metadata is extracted from HTTP responses if available. diff --git a/openwiki/ci-workflows.md b/openwiki/ci-workflows.md new file mode 100644 index 0000000000..adc2a0f7a5 --- /dev/null +++ b/openwiki/ci-workflows.md @@ -0,0 +1,362 @@ +--- +type: "Reference" +title: "CI/CD Workflows: GitHub Actions and Release Process" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-34e57b5a3a0c875639ab72a7 + resource: repo://.github/scripts/check_diff.py + - id: openwiki-source-f35e7c44cc1805709393a581 + resource: repo://.github/workflows/_lint.yml + - id: openwiki-source-c92cc62695c6def991956428 + resource: repo://.github/workflows/_release.yml + - id: openwiki-source-c9c292f4ecabe180cdae27ce + resource: repo://.github/workflows/_test_pydantic.yml + - id: openwiki-source-d8a8900818f4abab719bd1b7 + resource: repo://.github/workflows/_test_vcr.yml + - id: openwiki-source-4d9cccca7700db7220ec055e + resource: repo://.github/workflows/_test.yml + - id: openwiki-source-7330cb37457ccdb62d7c41c7 + resource: repo://.github/workflows/auto-label-by-package.yml + - id: openwiki-source-6e3a52c89729b5704dbd7eec + resource: repo://.github/workflows/check_diffs.yml + - id: openwiki-source-9069a5dd5fbb579fbd5470ce + resource: repo://.github/workflows/integration_tests.yml + - id: openwiki-source-6d4b4e707b8d60b6ccfa3425 + resource: repo://.github/workflows/openwiki-update.yml + - id: openwiki-source-f8781d847f6481a966a44a68 + resource: repo://.github/workflows/pr_labeler.yml +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +# CI/CD Workflows: GitHub Actions and Release Process + +LangChain employs a sophisticated CI/CD system built on GitHub Actions that automates testing, linting, quality checks, and release management across a monorepo structure. The system emphasizes efficiency through intelligent change detection, parallel matrix testing, and strict release gates. + +## Architecture Overview + +The CI/CD system consists of three layers: + +1. **Pull request / push CI (`check_diffs.yml`)**: Detects changed packages and runs targeted tests, linting, and compatibility checks +2. **Scheduled integration testing (`integration_tests.yml`)**: Daily remote API testing with live credentials against partner libraries +3. **Manual release workflow (`_release.yml`)**: Comprehensive pre-release validation, PyPI publishing, and dependent package testing + +## Primary CI Workflow (Pull Requests & Master Pushes) + +The main entry point is `.github/workflows/check_diffs.yml`, which runs on every pull request, push to master, and merge group event. + +### Change Detection & Matrix Generation + +The workflow begins with a change detection phase: + +1. A Python script (`.github/scripts/check_diff.py`) analyzes which files changed +2. Maps changes to package directories (`libs/core`, `libs/partners/*`, etc.) +3. Builds a dependency graph to include dependent packages when core components change +4. Generates separate test matrices for linting, unit tests, Pydantic compatibility tests, integration test compilation, VCR cassette tests, and extended test suites +5. Outputs are passed as JSON to downstream jobs via matrix strategy + +This detection ensures only affected packages are tested, optimizing CI runtime. + +### Linting Pipeline (`_lint.yml`) + +Runs on affected packages with Python 3.11 (configurable): + +- **Ruff analysis**: Code style, import sorting, and rule enforcement with inline GitHub annotations +- **MyPy type checking**: Static type verification +- **Markdown linting**: Documentation quality checks (via `.markdownlint.json`) + +Tools are sourced from dependency groups: `lint` and `typing`. The workflow installs both package code and test code dependencies, running `make lint_package` and `make lint_tests` targets. + +### Unit Testing (`_test.yml`) + +Runs matrix tests across Python versions with dependency constraint verification: + +**Matrix dimensions**: +- Python 3.10 through 3.14 (per-package configuration) +- Current locked dependencies (from `uv.lock`) +- Minimum supported dependency versions + +**Two-phase testing**: + +1. **Current dependencies**: Runs full test suite against versions in `uv.lock` +2. **Minimum dependencies**: Calculates minimum versions from `pyproject.toml` constraints, downgrades via pip, and reruns tests to ensure compatibility + +The workflow uses `make test PYTEST_EXTRA=-q` and `make tests PYTEST_EXTRA=-q` targets, and verifies the working directory remains clean (no untracked generated files). + +### Pydantic Compatibility Testing (`_test_pydantic.yml`) + +Tests affected packages against multiple Pydantic versions (e.g., v1 and v2 compatibility): + +- Triggered when Pydantic version constraints or dependent code changes +- Configurable per-package via `pyproject.toml` +- Runs matrix over specified Pydantic versions + +### VCR Cassette Tests (`_test_vcr.yml`) + +Validates integration tests backed by recorded HTTP cassettes: + +- Runs in playback-only mode (no API credentials required) +- Detects stale cassettes from test input changes without re-recording +- Enables fast, repeatable integration test feedback + +Only triggered for packages with VCR cassettes (currently `libs/partners/openai`). + +### Integration Test Compilation (`_compile_integration_test.yml`) + +Performs shallow integration test validation: + +- Compiles test modules without executing them +- Catches import errors and obvious syntax issues +- Provides quick feedback loop without running expensive external API calls + +### Extended Test Suites + +For packages defining `extended_testing_deps.txt`, runs additional tests: + +- Installs extra dependencies beyond standard test group +- Executes `make extended_tests` target +- Allows performance benchmarks, stress tests, or heavy-weight validations + +### Release Option Validation + +The workflow includes a `check-release-options` job: + +- Verifies `.github/workflows/_release.yml` dropdown options stay synchronized with actual package directories +- Prevents stale release options from blocking valid releases + +## Release Workflow (`_release.yml`) + +The release workflow is manually triggered via GitHub Actions UI (or can be called as a reusable workflow). It handles versioning, building, testing, and publishing to PyPI. + +### Release Modes & Invocation + +**Manual dispatch** (`workflow_dispatch`): +- Dropdown selection of package to release (core, langchain, langchain_v1, text-splitters, standard-tests, model-profiles, or partner packages) +- Manual version entry (default `0.1.0`) +- Optional override to full path (e.g., `libs/partners/partner-xyz`) +- Dangerous flags: `dangerous-nonmaster-release`, `allow-prereleases`, `skip-prior-published-package-checks` + +**Reusable workflow** (`workflow_call`): +- Accepts `working-directory`, `release-version`, and safety bypass flags +- Used internally for multi-package release orchestration + +### Release Gate: Build & Version Check + +**Job: `build`** (isolated permissions for security): + +1. **Version verification**: Checks `pyproject.toml` version against input, fails if mismatch +2. **PyPI availability check**: Queries PyPI to ensure version not already published (PEP 440 normalization applied) +3. **Build**: Runs `uv build` to create wheel and sdist distributions +4. **Artifact upload**: Stores `dist/` directory for downstream jobs + +Security rationale: Separates build (no credentials) from publishing (trusted publishing token) to prevent compromised dependencies from accessing PyPI credentials. + +### Release Notes Generation + +**Job: `release-notes`**: + +1. **Tag detection**: Finds previous release tag via git history + - For pre-releases: Matches base version; falls back to latest release + - For stable releases: Searches for previous patch version; falls back to latest +2. **Changelog extraction**: Runs `git log --format="%s" ..HEAD -- ` to collect commit messages +3. **First release handling**: Explicitly marks initial releases, uses full commit history + +### Pre-Release Checks + +**Job: `pre-release-checks`** (no caching to catch missing dependencies): + +1. **Direct wheel installation**: Installs built wheel directly (validates metadata) +2. **Package import test**: Verifies main module imports successfully +3. **Unit tests**: Runs full `make tests` against the wheel +4. **Minimum version testing**: Recalculates and tests minimum dependencies (skips serdes tests for speed) +5. **Prerelease dependency detection**: Fails if any dependencies use prerelease constraints (unless release itself is prerelease) +6. **Integration tests**: For partner packages only, runs `make integration_tests` with live API credentials + +### PyPI Publishing + +**Job: `test-pypi-publish`** (TestPyPI): +- Uses GitHub OpenID Connect (trusted publishing) +- Publishes to test.pypi.org for staging validation +- Tolerates duplicate versions (CI safety only) + +**Job: `publish`** (Production PyPI): +- Uses trusted publishing to production PyPI +- Only runs if all prior checks pass +- Creates GitHub Release with generated release notes + +### Compatibility Testing + +**Job: `test-prior-published-packages-against-new-core`**: +- Only runs for `libs/core` releases +- Tests previously-published partner packages (e.g., langchain-openai, langchain-anthropic) against new core +- Fetches latest partner tag from git, installs new core wheel, runs tests +- Can skip per-partner via `skip-prior-published-package-checks` input + +**Job: `test-dependents`**: +- Only runs for `libs/core` or `libs/langchain_v1` releases +- Checks external dependent packages (e.g., deepagents) +- Tests Python 3.11 and 3.13 +- Ensures breaking changes are caught before publish + +## Integration Testing (`integration_tests.yml`) + +Scheduled daily (1 PM UTC) with manual dispatch override capability. + +### Test Matrix Generation + +**Job: `compute-matrix`**: + +- **Default scope**: Tests 9 partner libraries (OpenAI, Anthropic, Fireworks, Groq, MistralAI, XAI, Google VertexAI, Google GenAI, AWS) +- **Python versions**: 3.10 and 3.14 by default; overridable via input +- **Selective testing**: Can select single library, exclude libraries, or override Python versions +- **Scope security**: Only runs on main repository; manual dispatch allowed from forks + +### Integration Test Execution + +**Job: `integration-tests`**: + +- Checks out primary monorepo plus external google-genai, google-vertexai, and langchain-aws repositories +- Reorganizes external repos into local partner directories for unified testing +- Authenticates to Google Cloud and AWS +- Runs per-package `make integration_tests` with all live API credentials injected +- Uses concurrency locks per (package, python-version) to serialize same-package runs and prevent credential conflicts + +**Credentials**: Receives 30+ environment variables covering OpenAI, Anthropic, Google, AWS, Azure, Groq, MistralAI, HuggingFace, and more. + +## Auto-Labeling Workflows + +### Issue Auto-Labeling (`auto-label-by-package.yml`) + +Fires when issues are opened or edited: + +1. Parses issue body for `## Package` section +2. Maps package name (e.g., "langchain-openai") to label (e.g., "openai") +3. Adds/removes labels to match selected package(s) +4. Supports both dropdown (single) and checkbox (multi-select) formats + +### PR Labeling (`pr_labeler.yml`) + +Unified PR labeler applying size, file-based, title-based, and contributor classification: + +- File-based labels: Maps changed file paths to package labels +- Size labels: Computes PR size (small, medium, large) from diff statistics +- Title-based labels: Detects certain patterns in PR title +- Contributor classification: Checks org membership to tag external contributions +- Uses GitHub App for organization membership verification + +Consolidates multiple prior workflows into single sequential run to eliminate race conditions. + +## OpenWiki Auto-Update (`openwiki-update.yml`) + +Runs on schedule (8 AM UTC daily) or manual dispatch: + +1. Checks out full repository history (required for diff-against-HEAD) +2. Installs Node.js and OpenWiki CLI +3. Runs `openwiki code --update --print` to regenerate documentation +4. Removes transient state file +5. Creates/updates pull request with changes +6. Preserves partial progress on failure for baseline establishment + +Uses LangSmith tracing for observability. + +## Dependency Pinning & Version Management + +### Frozen Dependency Locks + +All CI jobs set `UV_FROZEN=true` and `UV_NO_SYNC=true` (when applicable): + +- Ensures reproducible builds against locked versions in `uv.lock` +- Prevents transitive dependency surprises in CI +- Each job explicitly pins Python version and dependency revisions + +### Minimum Version Testing + +The `get_min_versions.py` script extracts version constraints from `pyproject.toml` and queries PyPI for minimum published versions satisfying those constraints. + +Example: If constraint is `langchain-core>=0.3.0,<1.0`, the script finds and installs the earliest 0.3.* release. + +Two modes: +- `pull_request`: Tests against minimum with some leniency (used in PR CI) +- `release`: Stricter testing with prerelease rejection (used in release validation) + +## Release Policy + +### Semantic Versioning + +**Core** (`libs/core`) follows strict semantic versioning: +- Major version: Breaking changes +- Minor version: New features (backward compatible) +- Patch version: Bug fixes + +**Partner packages** and other libraries align with core releases: +- LangChain follows core versioning for tight integration +- Partners maintain independent versioning but coordinate with core releases + +### Release Branching + +- Releases only proceed from `master` branch (default) or explicitly via `dangerous-nonmaster-release` flag (hotfixes only) +- Version must match `pyproject.toml` or operator provides override +- PyPI availability double-checked to prevent accidental re-publishes + +### Pre-Release Support + +- Supports alpha/beta/rc versions (e.g., `0.1.0-rc1`, `0.1.0a1`) +- Pre-release detection normalizes hyphen/underscore variants per PEP 440 +- Optional `allow-prereleases` flag permits transitive prerelease dependencies during alpha cycles +- Final releases block prerelease dependencies unless explicitly allowed + +## Configuration & Operations + +### Environment Variables + +**Frozen dependency control**: +- `UV_FROZEN`: Prevents automatic dependency resolution +- `UV_NO_SYNC`: Skips uv sync in build steps (manual sync used instead) + +**Linting & formatting**: +- `RUFF_OUTPUT_FORMAT: github`: Inline GitHub annotations for linter violations + +**LangSmith tracing** (optional): +- `LANGSMITH_API_KEY`: Optional tracing of CI workflows themselves +- `LANGCHAIN_TRACING_V2: true`: Enable tracing +- `LANGCHAIN_PROJECT: openwiki`: LangSmith project name + +### GitHub Actions Permissions + +Workflows follow principle of least privilege: + +- **Default**: `contents: read` (read-only) +- **PR labeler**: `pull-requests: write`, `issues: write` +- **Release**: `id-token: write` (trusted publishing), `contents: write` (GitHub Release creation) +- **OpenWiki update**: `contents: write`, `pull-requests: write` + +Isolated jobs (build, testing) receive no write permissions; publishing jobs run in separate jobs with restricted scope. + +### Custom Actions + +**`uv_setup`** (`.github/actions/uv_setup`): +- Sets up Python via official `setup-python` action +- Configures `uv` tool with optional caching +- Supports per-package cache suffixes to avoid cross-contamination +- Parameters: `python-version`, `cache-suffix`, `working-directory`, `enable-cache` + +## Important Invariants & Failure Modes + +1. **No caching in release pre-checks**: Missing dependencies would be masked by cached venvs, allowing broken releases to publish +2. **Minimum version downgrade isolation**: Minimum version tests reinstall packages in fresh virtual environment context, not via constraint relaxation alone +3. **Separate build/publish jobs**: Build job has no PyPI credentials; publishing job has no build tools, preventing supply-chain attacks +4. **Change detection scope**: VCR and extended test matrices only include packages with appropriate markers; adding test files without markers won't trigger corresponding test suites +5. **Prerelease blocking**: Stable releases reject any prerelease dependencies, preventing version resolution issues in downstream users +6. **Tag/version synchronization**: Release workflow validates git tags match expected version format before publishing, catching manual tag drift + +## Extension Points + +1. **Adding new package types**: Update `check_diff.py` to recognize new directories and map them to appropriate test matrices +2. **Adding partners to release testing**: Update `test-prior-published-packages-against-new-core` matrix and `skip-prior-published-package-checks` input options (keep in sync) +3. **Adding new linting/type checkers**: Extend `_lint.yml` job steps and dependency groups; ensure `make lint_package` target exists +4. **Adding integration test credentials**: Add environment variable to `integration_tests.yml` job and ensure `make integration_tests` target handles optional credentials +5. **Custom test suites**: Create `extended_testing_deps.txt` in package directory and define `make extended_tests` target +6. **OpenWiki pages**: Add to `openwiki/` directory; auto-updated on each scheduled run diff --git a/openwiki/composability.md b/openwiki/composability.md new file mode 100644 index 0000000000..a4da4512f1 --- /dev/null +++ b/openwiki/composability.md @@ -0,0 +1,400 @@ +--- +type: "Reference" +title: "Dict syntax creates a RunnableParallel" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-a1981e868973f6fd7f71e12e + resource: repo://libs/core/langchain_core/runnables/base.py + - id: openwiki-source-48e94bbe49ab4f33ba87e9cb + resource: repo://libs/core/langchain_core/runnables/branch.py + - id: openwiki-source-de6c904bd0171642bd50f6d9 + resource: repo://libs/core/langchain_core/runnables/router.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +Composability is the core feature of LangChain's Runnable protocol: the ability to declaratively chain, parallelize, and conditionally route components. Every composed chain automatically inherits sync (`invoke`), async (`ainvoke`), batch (`batch`/`abatch`), and streaming (`stream`/`astream`) capabilities—with optimizations for efficiency. + +The two main composition primitives are **`RunnableSequence`** (sequential chaining via the `|` operator) and **`RunnableParallel`** (parallel execution via dict syntax). Conditional routing is achieved with **`RunnableBranch`** and **`RouterRunnable`**. + +## Sequential Composition: The `|` Operator + +The **pipe operator** (`|`) chains Runnables in sequence, with each step's output becoming the next step's input. This is the most common composition pattern. + +```python +from langchain_core.runnables import RunnableLambda + +add_one = RunnableLambda(lambda x: x + 1) +mul_two = RunnableLambda(lambda x: x * 2) + +sequence = add_one | mul_two +sequence.invoke(1) # (1 + 1) * 2 = 4 +``` + +The `|` operator creates a **`RunnableSequence`**, which: +- Invokes each step in order, passing output to the next input +- Flattens nested sequences for efficiency +- Automatically preserves streaming properties if all steps support the `transform` method +- Supports both sync and async execution + +### Data Flow + +``` +Input → Step 1 → Step 2 → Step 3 → Output +``` + +When a dict is piped into a sequence, it becomes a **`RunnableParallel`**: + +```python +sequence = add_one | { + "mul_2": RunnableLambda(lambda x: x * 2), + "mul_5": RunnableLambda(lambda x: x * 5), +} +sequence.invoke(1) # {'mul_2': 4, 'mul_5': 10} +``` + +## Parallel Composition: Branching with `+` and Dict Syntax + +Parallel execution invokes multiple Runnables concurrently on the **same input**. This is achieved via dict literals within a sequence or directly with **`RunnableParallel`**. + +### Dict Literal Syntax + +```python +from langchain_core.runnables import RunnableLambda, RunnableParallel + +add_one = RunnableLambda(lambda x: x + 1) +mul_two = RunnableLambda(lambda x: x * 2) +mul_three = RunnableLambda(lambda x: x * 3) + +# Dict syntax creates a RunnableParallel +sequence = add_one | { + "mul_2": mul_two, + "mul_3": mul_three, +} +sequence.invoke(1) +# Output: {'mul_2': 4, 'mul_3': 6} +``` + +### Explicit RunnableParallel + +```python +parallel = RunnableParallel( + mul_2=mul_two, + mul_3=mul_three, +) +parallel.invoke(2) +# Output: {'mul_2': 4, 'mul_3': 6} +``` + +### Concurrent Execution + +- **`RunnableParallel`** creates independent input copies for each branch using `atee` (async) or `safetee` (sync) +- Each branch executes concurrently, with chunks yielded in the order they complete +- For async streaming, tasks are managed with `asyncio.wait(return_when=FIRST_COMPLETED)` to emit output as soon as any branch produces a chunk +- The final result is a dict combining outputs from all branches + +## Batching: Parallel Invocation over Multiple Inputs + +Batching processes multiple inputs efficiently through a pipeline. Unlike parallel branching, batching applies the **same sequence** to each input in parallel. + +### Sync Batch + +```python +sequence = add_one | mul_two +results = sequence.batch([1, 2, 3]) +# [4, 6, 8] # Each input processed in parallel via thread pool +``` + +### Async Batch + +```python +results = await sequence.abatch([1, 2, 3]) +# [4, 6, 8] +``` + +### Implementation + +- Default `batch` uses a thread pool executor via `get_executor_for_config` +- `abatch` uses `asyncio.gather` with concurrency control via `max_concurrency` +- Each step in the sequence batches its inputs independently +- **`RunnableSequence`** calls `batch` on each step in order, feeding outputs to the next + +## Streaming: Token-by-Token Output + +Streaming emits output chunks as they are produced, enabling real-time responses from LLMs and other sequential generators. + +### Stream Method + +```python +for chunk in sequence.stream(1): + print(chunk) # Intermediate outputs as they become available +``` + +### Astream Method (Async) + +```python +async for chunk in sequence.astream(1): + print(chunk) # Non-blocking iteration +``` + +### Streaming Pipeline + +A **`RunnableSequence`** preserves streaming properties: +- If all steps implement `transform` (which processes `Iterator[Input] → Iterator[Output]`), streaming passes through the entire pipeline +- If any step doesn't support `transform`, streaming blocks until that step completes, then resumes +- **`RunnableLambda`** does not implement `transform` by default; use **`RunnableGenerator`** for custom streaming logic + +### Example: Prompt → Model → Parser + +```python +from langchain_core.prompts import ChatPromptTemplate +from langchain_openai import ChatOpenAI +from langchain_core.output_parsers import StrOutputParser + +prompt = ChatPromptTemplate.from_template("What is {topic}?") +model = ChatOpenAI() +parser = StrOutputParser() + +chain = prompt | model | parser + +# Stream tokens as the model generates them +for chunk in chain.stream({"topic": "composability"}): + print(chunk, end="", flush=True) +``` + +In this chain: +1. `ChatPromptTemplate` formats the input dict into a string prompt +2. `ChatOpenAI` streams tokens as they arrive from the API +3. `StrOutputParser` passes tokens through unchanged + +Tokens flow end-to-end without waiting for the full response. + +## Conditional Routing: RunnableBranch and RouterRunnable + +Conditional logic routes inputs to different branches based on predicates. + +### RunnableBranch: Predicate-Based Routing + +A **`RunnableBranch`** evaluates conditions in order and executes the first matching branch: + +```python +from langchain_core.runnables import RunnableBranch, RunnableLambda + +branch = RunnableBranch( + (lambda x: isinstance(x, int), RunnableLambda(lambda x: x * 2)), + (lambda x: isinstance(x, str), RunnableLambda(lambda x: x.upper())), + RunnableLambda(lambda x: "unknown"), +) + +branch.invoke(5) # 10 +branch.invoke("hello") # "HELLO" +branch.invoke(None) # "unknown" +``` + +Conditions are evaluated sequentially; the first truthy result selects its corresponding Runnable. If no condition matches, the default branch executes. + +### RouterRunnable: Key-Based Routing + +A **`RouterRunnable`** routes based on a string key in the input: + +```python +from langchain_core.runnables.router import RouterRunnable + +add = RunnableLambda(lambda x: x + 1) +square = RunnableLambda(lambda x: x ** 2) + +router = RouterRunnable(runnables={"add": add, "square": square}) +router.invoke({"key": "square", "input": 3}) # 9 +router.invoke({"key": "add", "input": 3}) # 4 +``` + +The input is a dict with `"key"` (which Runnable to route to) and `"input"` (the data). + +## Composition with RunnablePassthrough + +**`RunnablePassthrough`** forwards inputs unchanged or with additional keys, useful for preserving context in parallel branches: + +```python +from langchain_core.runnables import RunnablePassthrough + +chain = ( + RunnableLambda(lambda x: x + 1) + | { + "original": RunnablePassthrough(), + "modified": RunnableLambda(lambda x: x * 2), + } +) + +chain.invoke(5) +# {'original': 6, 'modified': 12} +``` + +Here, the passthrough preserves the intermediate result for reuse by another branch. + +## Async Equivalents + +Every method has an async counterpart: + +| Sync | Async | +|------|-------| +| `invoke(input)` | `ainvoke(input)` | +| `batch(inputs)` | `abatch(inputs)` | +| `stream(input)` | `astream(input)` | +| `transform(Iterator[Input])` | `atransform(AsyncIterator[Input])` | + +Async methods integrate with the callback system and execute concurrency-aware batching via `asyncio.gather`. + +## Chaining Patterns + +### Common Pattern: Prompt → Model → Parser + +```python +from langchain_core.prompts import ChatPromptTemplate +from langchain_openai import ChatOpenAI +from langchain_core.output_parsers import StrOutputParser + +chain = ( + ChatPromptTemplate.from_template("What is {topic}?") + | ChatOpenAI() + | StrOutputParser() +) + +# Single invoke +output = chain.invoke({"topic": "LLMs"}) + +# Batch process +outputs = chain.batch([{"topic": "LLMs"}, {"topic": "Vectors"}]) + +# Stream tokens +for chunk in chain.stream({"topic": "LLMs"}): + print(chunk, end="", flush=True) +``` + +### Fan-Out / Fan-In: Parallel Processing + +```python +from langchain_core.runnables import RunnableLambda, RunnablePassthrough + +chain = ( + RunnablePassthrough() + | { + "summary": RunnableLambda(summarize), + "entities": RunnableLambda(extract_entities), + "sentiment": RunnableLambda(analyze_sentiment), + } +) + +result = chain.invoke(text) +# {'summary': '...', 'entities': [...], 'sentiment': 'positive'} +``` + +### Conditional Execution + +```python +from langchain_core.runnables import RunnableBranch + +route_logic = RunnableBranch( + (lambda x: "math" in x.lower(), math_chain), + (lambda x: "code" in x.lower(), code_chain), + general_chain, +) + +output = route_logic.invoke("How do I calculate factorial?") +``` + +## Type Safety and Schema Inference + +Chains infer input and output types from their components: + +```python +sequence = add_one | mul_two + +# Access inferred schemas +print(sequence.input_schema) # Pydantic model for input +print(sequence.output_schema) # Pydantic model for output +print(sequence.input_schema.model_json_schema()) +``` + +This enables validation and documentation without explicit type annotations. + +## Optimization and Flattening + +**`RunnableSequence`** automatically flattens nested sequences: + +```python +# These are equivalent: +chain1 = step1 | step2 | step3 +chain2 = step1 | (step2 | step3) +chain3 = (step1 | step2) | step3 +``` + +All produce a single flat sequence with steps `[step1, step2, step3]`, avoiding unnecessary nesting overhead. + +## Serialization and Debugging + +Composed chains support serialization via the LangChain serialization system, enabling: +- **Persistence**: Save and load chains +- **Tracing**: Automatic callback integration for debugging via LangSmith +- **Inspection**: Use `get_graph()` to visualize chain structure + +Enable debug output: + +```python +from langchain_core.globals import set_debug + +set_debug(True) # Print intermediate results +chain.invoke(input) + +# Or use callbacks: +from langchain_core.tracers import ConsoleCallbackHandler + +chain.invoke(input, config={"callbacks": [ConsoleCallbackHandler()]}) +``` + +## Extension: Custom Runnables + +Implement **`Runnable`** to create custom components: + +```python +from langchain_core.runnables import Runnable, RunnableConfig +from typing import Iterator + +class CustomRunnable(Runnable[str, int]): + def invoke(self, input: str, config: RunnableConfig | None = None) -> int: + return len(input) + + async def ainvoke(self, input: str, config: RunnableConfig | None = None) -> int: + return len(input) + + def stream(self, input: str, config: RunnableConfig | None = None) -> Iterator[int]: + # For streaming support, implement transform + for char in input: + yield 1 + + async def astream(self, input: str, config: RunnableConfig | None = None): + for char in input: + yield 1 + +# Immediately composable +chain = CustomRunnable() | another_step +``` + +Custom Runnables are automatically compatible with all composition operators. + +## Summary Table + +| Operator | Effect | Example | +|----------|--------|---------| +| `\|` | Sequential chaining | `step1 \| step2` | +| Dict in sequence | Parallel branching | `step1 \| {key1: step2, key2: step3}` | +| `RunnableBranch` | Conditional routing | `RunnableBranch((cond, runnable), default)` | +| `RouterRunnable` | Key-based routing | `RouterRunnable({"key": runnable})` | +| `.batch()` / `.abatch()` | Parallel input processing | `chain.batch([in1, in2])` | +| `.stream()` / `.astream()` | Token-by-token output | `for chunk in chain.stream(input):` | + +See the [Runnables](runnables.md) page for protocol details and method signatures. diff --git a/openwiki/dev-commands.md b/openwiki/dev-commands.md new file mode 100644 index 0000000000..2bdd310e02 --- /dev/null +++ b/openwiki/dev-commands.md @@ -0,0 +1,393 @@ +--- +type: "Developer Tools & Commands" +title: "Development Commands and Local Setup" +description: "Quick reference for uv, make, lint, test, and type-checking commands in the LangChain monorepo, including environment setup, pre-commit hooks, and testing workflows." +tags: [development, build, testing, linting, typing, uv, make, pre-commit, local-setup] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-4d1645cb6317345817452838 + resource: repo://.pre-commit-config.yaml + - id: openwiki-source-a2371d6362e5db4bc834ad03 + resource: repo://CLAUDE.md + - id: openwiki-source-8f1875229ad4a704c8e20a06 + resource: repo://libs/core/Makefile + - id: openwiki-source-3486a94e6eb23a78271a5bfb + resource: repo://libs/core/pyproject.toml + - id: openwiki-source-7c96a74af67942d40559bf7d + resource: repo://libs/langchain_v1/Makefile + - id: openwiki-source-f708a9db48bfcf1b154e4708 + resource: repo://libs/langchain/Makefile + - id: openwiki-source-49fbcc45434b619b68220bf9 + resource: repo://libs/Makefile + - id: openwiki-source-a6e669bb11f217c6fbd06670 + resource: repo://libs/partners/anthropic/Makefile +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +The LangChain Python monorepo uses `uv` for dependency management, `make` for task automation, and `ruff`/`mypy` for code quality. This page provides a quick reference for common development commands and setup workflows. + +## Initial Setup + +### Install Dependencies + +Each package in `libs/` has its own `pyproject.toml` and `uv.lock`. Before running tests or making changes, set up dependencies: + +```bash +# Install all dependency groups (lint, typing, test, dev) +uv sync --all-groups + +# Or install only a specific group +uv sync --group test +uv sync --group lint +``` + +The `--all-groups` flag ensures you have tools for linting, type checking, and testing. See the [Contributing Guide in CLAUDE.md](repo://CLAUDE.md) for detailed development conventions and PR guidelines. + +### Pre-Commit Setup + +The repository uses pre-commit hooks to enforce code quality on commit. Install and configure them once: + +```bash +pre-commit install +``` + +Pre-commit runs automatically on staged files before each commit. To manually trigger hooks: + +```bash +# Run all hooks on all files +pre-commit run --all-files + +# Run a specific hook +pre-commit run ruff --all-files +``` + +## Pre-Commit Hooks + +The `.pre-commit-config.yaml` defines hooks that enforce: + +- **Standard validation**: YAML/TOML syntax checking, proper file endings, no trailing whitespace +- **Text normalization**: Fix curly quotes and non-standard spaces +- **Per-package format and lint**: Each package in `libs/` (core, langchain, partners/*) runs `make format lint` +- **Version consistency checks**: Ensure `pyproject.toml` versions match source code for `langchain-core`, `langchain`, and partner packages + +These hooks automatically prevent commits that fail linting or have formatting issues. They use the same Makefiles documented below. + +## Testing Commands + +### Run All Unit Tests + +```bash +# From any package directory +make test + +# Example: test langchain_v1 +cd libs/langchain_v1 && make test +``` + +Unit tests live in `tests/unit_tests/` (no network calls allowed). The test target uses `pytest` with `xdist` for parallelization and socket restrictions to prevent accidental network calls. + +### Run a Specific Test File + +```bash +# Using make +make test TEST_FILE=tests/unit_tests/agents/test_agent.py + +# Or using uv directly +uv run --group test pytest tests/unit_tests/agents/test_agent.py +``` + +### Integration Tests + +Integration tests live in `tests/integration_tests/` and require network access and API keys. Run them separately: + +```bash +make integration_tests +``` + +Some packages (like `langchain_v1`) use Docker services (PostgreSQL, Redis) for integration tests: + +```bash +cd libs/langchain_v1 +make test # starts services, runs tests, stops services +``` + +### Test in Watch Mode + +Auto-re-run tests as you edit code: + +```bash +make test_watch +``` + +This uses `pytest-watcher` (via the `ptw` command) and updates snapshots automatically. + +### Coverage Reports + +Generate code coverage reports: + +```bash +make coverage +``` + +This produces `xml` and term-missing reports, useful for understanding untested code paths. + +## Linting and Formatting + +### Run Full Linting Suite + +```bash +make lint +``` + +This runs three checks in order: +1. **Ruff check**: Linter for logical errors, naming conventions, and code smells +2. **Ruff format (diff)**: Format checking (does not modify files) +3. **Mypy**: Static type checking + +### Format Code + +```bash +make format +``` + +This applies `ruff format` and `ruff check --fix` to auto-fix formatting and logical issues (e.g., unsorted imports, unused variables). + +### Ruff-Only Commands + +Format and linter can be run separately for faster iteration: + +```bash +# Check formatting without fixing +ruff check . +ruff format . --diff + +# Fix formatting and lint issues +ruff check . --fix +ruff format . +``` + +Run from within a package directory or from the repo root. Ruff processes Python and Jupyter notebooks. + +### Type Checking + +Full type checking with mypy: + +```bash +mypy . +``` + +Or use the make target: + +```bash +make type +``` + +This checks all Python files for type errors (e.g., incorrect argument types, missing type hints). Type checking can be slow for large packages; run it with: + +```bash +# Type check specific file or directory +mypy libs/core/langchain_core/runnables.py +``` + +## Testing a Single Package + +To develop and test a single package in the monorepo: + +```bash +# Example: work on langchain_v1 core agent system +cd libs/langchain_v1 + +# Install all dependencies for this package +uv sync --all-groups + +# Run all unit tests +make test + +# Test specific file or with custom pytest options +make test TEST_FILE=tests/unit_tests/agents/test_create_agent.py + +# Format and lint +make format +make lint + +# Type check +make type +``` + +Each package has its own Makefile with consistent targets. The monorepo's `/libs/Makefile` provides package-wide commands like regenerating lock files. + +## Lock File Management + +The `uv.lock` file in each package pins exact dependency versions for reproducible builds. Update locks when dependencies change: + +```bash +# From a package directory +uv lock + +# Or regenerate all package locks +cd libs && make lock + +# Verify all locks are up-to-date (CI check) +cd libs && make check-lock +``` + +The `.pre-commit-config.yaml` includes `UV_FROZEN = true`, which prevents unexpected lock file changes during regular development. Use the commands above when intentionally updating dependencies. + +## Make Commands Reference + +All packages follow the same Makefile structure: + +| Command | Purpose | +|---------|---------| +| `make test` | Run all unit tests (pytest) | +| `make test TEST_FILE=` | Run tests in a specific file or directory | +| `make test_watch` | Run tests in watch mode (auto-rerun on changes) | +| `make integration_tests` | Run integration tests (requires API keys) | +| `make extended_tests` | Run only tests marked with `@pytest.mark.extended` | +| `make lint` | Run ruff check + ruff format --diff + mypy | +| `make format` | Apply ruff format and ruff check --fix | +| `make type` | Run mypy type checking | +| `make coverage` | Run tests and generate coverage report | +| `make help` | Display all available targets | + +Package-specific commands (see Makefiles in each directory): + +- `langchain_v1`: `make test_fast`, `make coverage_agents`, `make start_services`, `make stop_services` +- `core`: `make check_imports`, `make benchmark` +- `partners/*`: `make test TEST_FILE=tests/integration_tests/` + +## Common Workflows + +### Before Committing + +```bash +# 1. Format code +make format + +# 2. Run linting and type checks +make lint + +# 3. Run tests +make test + +# 4. Commit (pre-commit hooks will run automatically) +git commit +``` + +Or let pre-commit do the format/lint: + +```bash +make test +git add . +pre-commit run --all-files # or just commit and let hooks run +git commit +``` + +### Iterative Development + +For fast feedback during development: + +```bash +# Terminal 1: Watch tests +make test_watch + +# Terminal 2: Edit code and format +# Changes auto-trigger re-run in Terminal 1 +make format +``` + +### Linting a Changed File + +```bash +# Lint only files changed in the current branch +make lint_diff +make format_diff +``` + +These targets run on `git diff` output against `master`. + +### Type Checking Specific Modules + +```bash +# Type check a module while developing +mypy libs/langchain_v1/langchain/agents/agent.py + +# Type check tests (faster, uses test group) +cd libs/langchain_v1 +make lint_tests +``` + +## Environment Variables + +The Makefiles use a few environment variables to control behavior: + +| Variable | Purpose | Default | +|----------|---------|---------| +| `UV_FROZEN` | Prevent lock file changes during `uv sync` | `true` in Makefiles | +| `TEST_FILE` | Path to test file or directory | `tests/unit_tests/` | +| `PYTEST_EXTRA` | Extra pytest options | (empty) | +| `LANGGRAPH_TEST_FAST` | Use in-memory services instead of Docker | `1` (fast) or `0` (full) | + +Example: Run fast tests with extra pytest verbosity: + +```bash +make test PYTEST_EXTRA="-vv" TEST_FILE=tests/unit_tests/agents +``` + +## Troubleshooting + +### Lock file out of sync + +```bash +# Regenerate lock +cd libs/ +uv lock + +# Or check if lock is up-to-date +uv lock --check +``` + +### Dependencies not installed + +```bash +# Ensure all groups are installed +uv sync --all-groups + +# Or just the test group +uv sync --group test +``` + +### Tests fail with "no network" error + +This is intentional—unit tests have socket restrictions. For integration tests: + +```bash +make integration_tests +``` + +### Ruff or mypy not found + +```bash +# Install lint and typing groups +uv sync --group lint --group typing +``` + +### Pre-commit hook fails locally but passes in CI + +Ensure you're using the same Python version and have all dependency groups installed: + +```bash +python --version +uv sync --all-groups +pre-commit run --all-files +``` + +## Related Documentation + +- [Contributing Guide](repo://CLAUDE.md): Detailed development conventions, PR templates, and code standards +- [System Architecture](repo:///openwiki/architecture.md): Three-layer design and module responsibilities +- [CI/CD Workflows](repo:///openwiki/ci-workflows.md): GitHub Actions automation and release process diff --git a/openwiki/index.md b/openwiki/index.md new file mode 100644 index 0000000000..0dd7b7a236 --- /dev/null +++ b/openwiki/index.md @@ -0,0 +1,29 @@ +--- +okf_version: "0.2" +--- + +# Files + +- [Agent Execution Flow and Loop Control](agent-execution.md) - Traces the runtime lifecycle of an agent from user input through model invocation, tool dispatch, and loop termination conditions, with detailed state management and middleware integration points. +- [Create a basic agent](agent-factory.md) +- [LangChain System Architecture](architecture.md) - High-level decomposition of the LangChain framework into three layers: langchain-core (abstractions), langchain (orchestration and agents), and partners (provider integrations), showing dependencies, component responsibilities, and extension boundaries. +- [> Entering new SequentialChain chain...](callbacks.md) +- [Chat Model Interface and Lifecycle](chat-models.md) - Document BaseChatModel protocol, input/output handling, streaming, and integration points with callbacks and model profiling. +- [CI/CD Workflows: GitHub Actions and Release Process](ci-workflows.md) +- [Dict syntax creates a RunnableParallel](composability.md) +- [Development Commands and Local Setup](dev-commands.md) - Quick reference for uv, make, lint, test, and type-checking commands in the LangChain monorepo, including environment setup, pre-commit hooks, and testing workflows. +- [Integration Testing: Live API Tests and VCR Cassettes](integration-tests.md) - How to write integration tests that call real model APIs with VCR cassette recording for CI compatibility, including environment setup, cassette management, and parameterization patterns. +- [Bearer token](mcp-integration.md) +- [Message Types and Content Representation](messages.md) - Document the message abstraction, standardized content blocks for multimodal LLM I/O, message hierarchy, and provider-specific block translators. +- [Middleware](middleware.md) +- [Chat Model Initialization with init_chat_model](model-initialization.md) - Factory function for instantiating chat models from provider strings with unified configuration and runtime model switching. +- [Use async methods (ainvoke, astream)](openai-provider.md) +- [Adding a New Chat Model Provider](partner-pattern.md) - Step-by-step guide to integrate a new LLM provider into LangChain's monorepo, including package structure, ChatModel implementation, streaming, function calling, and standard tests. +- [Prompt Templates and Few-Shot Learning](prompts.md) - Prompt templates define message sequences and variable substitution patterns for chat models. Few-shot learning selects examples dynamically to teach models by example. +- [LangChain Repository Quick Start](quickstart.md) - Entry point for engineers: orient to the monorepo structure, run first tests, understand what to edit for common tasks, and route to major development areas. +- [Runnable: Core Composition Layer](runnables.md) - Explain the Runnable protocol and how it enables composable chaining of LLM components through the LangChain Expression Language (LCEL). +- [Source Map: Repository File Organization](source-map.md) - Quick reference for locating code by topic, mapping LangChain concepts to their implementation paths across the monorepo including core abstractions, agents, middleware, partners, and configuration files. +- [Streaming: Token-by-Token Output](streaming.md) - How streaming works across LLM components and chains, token-by-token delivery via AIMessageChunk, callback integration, and memory/latency tradeoffs. +- [AutoStrategy (recommended)](structured-output.md) +- [Form 1: No arguments (name from function)](tools.md) +- [Unit Testing: Strategies and Patterns](unit-tests.md) - How to write unit tests for langchain-core and langchain components using pytest, fixtures, mocking, and standard test classes from langchain-tests. diff --git a/openwiki/integration-tests.md b/openwiki/integration-tests.md new file mode 100644 index 0000000000..9632ed0758 --- /dev/null +++ b/openwiki/integration-tests.md @@ -0,0 +1,687 @@ +--- +type: "Testing & QA" +title: "Integration Testing: Live API Tests and VCR Cassettes" +description: "How to write integration tests that call real model APIs with VCR cassette recording for CI compatibility, including environment setup, cassette management, and parameterization patterns." +tags: [integration-tests, vcr, cassettes, api-testing, pytest, ci-cd, model-testing] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-bcf7be66f36f862f639f3c7a + resource: repo://libs/langchain_v1/tests/integration_tests/conftest.py + - id: openwiki-source-bae620f3bb8d2668c69ac079 + resource: repo://libs/langchain/tests/integration_tests/.env.example + - id: openwiki-source-9a9f29414e5bd045f28c0081 + resource: repo://libs/partners/openai/Makefile + - id: openwiki-source-df762860acfcc6abf0ce804b + resource: repo://libs/partners/openai/pyproject.toml + - id: openwiki-source-ed8ac34fd0216d557729038e + resource: repo://libs/partners/openai/tests/conftest.py + - id: openwiki-source-ce605960b642234837131282 + resource: repo://libs/partners/openai/tests/integration_tests/chat_models/conftest.py + - id: openwiki-source-711811fb8df6b32329091d23 + resource: repo://libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py + - id: openwiki-source-d619d2c7cfe653fb9b712740 + resource: repo://libs/partners/openai/tests/integration_tests/chat_models/test_base.py + - id: openwiki-source-e758f7cc743f83c081224f08 + resource: repo://libs/partners/openai/tests/integration_tests/embeddings/test_base.py + - id: openwiki-source-db02c1dda8563ab005cd9d62 + resource: repo://libs/standard-tests/langchain_tests/conftest.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +Integration tests in LangChain differ fundamentally from unit tests: they call real model APIs (OpenAI, Anthropic, etc.) and require network access and valid credentials. To make these tests reproducible and CI-friendly without exposing API keys or relying on external services, LangChain uses **VCR cassettes**—recorded HTTP interactions that are replayed during test execution. + +This page covers the full lifecycle of integration testing: setting up environments, understanding the VCR pattern, organizing cassettes, writing tests that work with both live and recorded modes, handling parameterization, and managing cassette refresh workflows. + +## Environment Setup: API Keys and .env + +Integration tests require valid API credentials to record cassettes (once) and for running live integration tests in scheduled or on-demand scenarios. Store credentials in a `.env` file at the integration test directory root. + +### .env File Location and Format + +For most packages (e.g., OpenAI partner), the structure is: + +``` +libs/partners/openai/tests/.env +``` + +Example `.env.example` (visible in repo): + +```bash +# openai +# your api key from https://platform.openai.com/account/api-keys +OPENAI_API_KEY=your_openai_api_key_here + +# searchapi +SEARCHAPI_API_KEY=your_searchapi_api_key_here +``` + +**Important**: `.env` files are `.gitignore`d and never committed. Copy `.env.example` to `.env` locally and populate with valid keys. + +### Automatic Skipping When Keys Are Missing + +Integration tests don't explicitly skip when API keys are absent. Instead: + +1. Tests marked `@pytest.mark.scheduled` (for live API calls) run **only** in scheduled CI or when explicitly selected +2. Tests marked `@pytest.mark.vcr` (for cassette playback) run in CI via `make test_vcr` without credentials +3. Tests without markers but that consume API keys will fail at runtime if no `.env` is present—this is intentional for development environments + +When running `make integration_tests` locally, you must have a valid `OPENAI_API_KEY` in `.env`. If you don't, the test will error, alerting you that credentials are needed. + +### Environment Loading + +The test conftest automatically loads `.env` on import: + +```python +from pathlib import Path +from dotenv import load_dotenv + +PROJECT_DIR = Path(__file__).resolve().parent.parent + +def _load_env() -> None: + dotenv_path = PROJECT_DIR / "tests" / ".env" + if dotenv_path.exists(): + load_dotenv(dotenv_path) + +_load_env() +``` + +Credentials are then accessed via `os.environ["OPENAI_API_KEY"]` or similar in tests. + +## The VCR Pattern: Recording and Playback + +**VCR** (Video Cassette Recorder, implemented by the `vcrpy` library) records HTTP interactions—requests and responses—the first time a test runs with a live API. On subsequent runs, VCR replays the recorded cassette instead of making live network calls. + +### Recording Phase (Once, by Developers) + +When a new integration test is written or an existing test's behavior changes: + +1. Run the test with `--record-mode=new_episodes` (or the default `once`) and a valid API key in `.env` +2. VCR intercepts all HTTP calls your test makes +3. **Before recording**, sensitive headers and tokens in request/response bodies are **scrubbed** (redacted to `PLACEHOLDER` or `**REDACTED**`) +4. The cassette is saved as a YAML file (optionally compressed as `.yaml.gz`) +5. The cassette is committed to the repository + +### Playback Phase (Always, in CI and Local Development) + +When running tests: + +1. VCR loads the cassette file +2. Instead of making real API calls, VCR intercepts your HTTP client and returns the pre-recorded response +3. No API key is needed; no credentials are exposed +4. Tests are fast (no network latency) and deterministic (same response every time) + +### Cassette Location + +Cassettes are stored relative to test modules in a `cassettes/` subdirectory: + +``` +libs/partners/openai/tests/ + integration_tests/ + chat_models/ + test_base.py + cassettes/ + test_base/ + TestChatOpenAICodexStandard.test_invoke.yaml.gz + test_chat_openai.yaml.gz +``` + +Or at the test root: + +``` +libs/partners/openai/tests/cassettes/ + test_langchain_openai_embeddings_equivalent_to_raw.yaml.gz + test_streaming_tool_call_v1_v2_parity.yaml.gz +``` + +The fixture `vcr_cassette_dir` (from `conftest.py`) computes the correct directory per test module: + +```python +@pytest.fixture(scope="module") +def vcr_cassette_dir(request: pytest.FixtureRequest) -> str: + module = Path(request.module.__file__) + return str(module.parent / "cassettes" / module.stem) +``` + +## Security: Scrubbing Sensitive Data + +VCR cassettes are **committed to git** and **visible to the world** (in public repositories). To prevent accidental exposure of API keys, JWTs, OAuth tokens, and other secrets, LangChain uses a multi-layer scrubbing pipeline. + +### What Gets Redacted + +**Headers** (configured in `conftest.py`): + +```python +_EXTRA_HEADERS = [ + ("openai-organization", "PLACEHOLDER"), + ("user-agent", "PLACEHOLDER"), + ("authorization", "PLACEHOLDER"), + ("cookie", "PLACEHOLDER"), +] +``` + +**Request and Response Bodies** (OAuth secret fields): + +```python +_OAUTH_SECRET_FIELDS = frozenset({ + "access_token", + "refresh_token", + "id_token", + "code", + "device_code", + "client_secret", +}) +``` + +Redaction is applied with specialized handlers: + +- **JSON bodies**: Recursively walks the parsed JSON tree and redacts any field matching `_OAUTH_SECRET_FIELDS` +- **Form-encoded bodies**: Splits on `&` and redacts matching keys +- **JWT patterns**: Uses regex to detect and redact JWT-shaped strings anywhere in the body + +**Binary payloads** (PNG, JPEG, PDF, audio, etc.) are **skipped**—their magic bytes are detected and the scrubbing stack is bypassed for performance (JWTs and OAuth secrets are ASCII, so binary bodies can't carry them). + +### Scrubbing Configuration in conftest.py + +```python +def remove_request_headers(request: Any) -> Any: + """Remove sensitive headers and OAuth secrets from the request.""" + for k in request.headers: + request.headers[k] = "**REDACTED**" + request.uri = "**REDACTED**" + request.body = _scrub_oauth_secrets(request.body) + return request + +@pytest.fixture(scope="session") +def vcr_config() -> dict: + """Extend the default configuration coming from langchain_tests.""" + config = base_vcr_config() + config["match_on"] = ["json_body"] # Don't match on URI (it's redacted) + config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) + config["before_record_request"] = remove_request_headers + config["before_record_response"] = remove_response_headers + config["serializer"] = "yaml.gz" # Compress cassettes + return config +``` + +## Writing Integration Tests + +### Basic Structure + +Integration tests live in `tests/integration_tests/` (mirroring the layout of unit tests): + +``` +libs/partners/openai/tests/ + unit_tests/ + integration_tests/ + chat_models/ + __init__.py + conftest.py + test_base.py + cassettes/ +``` + +A minimal integration test using pytest-recording (which auto-hooks VCR): + +```python +import pytest +from langchain_openai import ChatOpenAI +from langchain_core.messages import HumanMessage + +@pytest.mark.vcr # Mark test to use VCR +def test_chat_openai_invoke(): + chat = ChatOpenAI(model="gpt-4o-mini") + response = chat.invoke([HumanMessage(content="Hello")]) + assert response.content # Assert the response is non-empty +``` + +The `@pytest.mark.vcr` marker tells pytest-recording to automatically: + +1. Look for a cassette file named `test_chat_openai_invoke.yaml.gz` in the module's `cassettes/` directory +2. Use VCR to intercept HTTP calls +3. Replay the cassette if it exists; record a new one if it doesn't (or if `--record-mode=new_episodes` is passed) + +### Markers for Test Classification + +**`@pytest.mark.scheduled`**: Marks tests for **live API calls** (no cassette). These run in scheduled CI workflows with real credentials. + +```python +@pytest.mark.scheduled +def test_chat_openai_streaming_live(): + """Test streaming with a live API call.""" + chat = ChatOpenAI(model="gpt-4o-mini", streaming=True) + response = chat.invoke("Hello") + assert response.content +``` + +These tests are **skipped in CI** unless explicitly selected or running in a scheduled job. Locally, they require a valid API key. + +**`@pytest.mark.vcr`**: Marks tests for **cassette playback**. VCR is enabled; the cassette is replayed. No API key needed in CI. + +```python +@pytest.mark.vcr +def test_chat_openai_with_tools(): + """Test tool calling with a cassette.""" + chat = ChatOpenAI(model="gpt-4o-mini") + tools = [...] + response = chat.invoke(..., tools=tools) + assert response.tool_calls +``` + +**No marker**: Tests without a marker run as **plain unit-like tests**, often for initialization, validation, or offline scenarios. They don't require an API key. + +```python +def test_chat_openai_model_name(): + """Test model name is set correctly.""" + chat = ChatOpenAI(model="gpt-4o-mini") + assert chat.model_name == "gpt-4o-mini" +``` + +### Parameterized Integration Tests + +When testing the same scenario against multiple models or configurations, use `pytest.mark.parametrize`: + +```python +import pytest +from langchain_openai import ChatOpenAI +from langchain_core.messages import HumanMessage + +MODELS = ["gpt-4o-mini", "gpt-4o"] + +@pytest.mark.vcr +@pytest.mark.parametrize("model_name", MODELS) +def test_chat_models_invoke(model_name: str): + """Test invoke on different models.""" + chat = ChatOpenAI(model=model_name) + response = chat.invoke([HumanMessage(content="Hello")]) + assert response.content +``` + +VCR automatically generates separate cassette files for each parameter combination: + +``` +cassettes/ + test_chat_models_invoke[gpt-4o-mini].yaml.gz + test_chat_models_invoke[gpt-4o].yaml.gz +``` + +When the test runs with `model_name="gpt-4o-mini"`, VCR loads the cassette matching `[gpt-4o-mini]`. This allows testing multiple models with independent recorded interactions. + +### Testing Streaming + +Stream tests benefit from cassettes because they capture the full stream sequence: + +```python +@pytest.mark.vcr +def test_chat_openai_streaming(): + """Test streaming behavior.""" + chat = ChatOpenAI(model="gpt-4o-mini") + chunks = [] + for chunk in chat.stream("Hello"): + chunks.append(chunk) + assert len(chunks) > 0 + assert chunks[-1].content # Final chunk has content +``` + +The cassette records all the streaming HTTP chunks, and VCR replays them in order during playback. + +### Testing Error Paths + +To test error handling (rate limits, malformed inputs, API errors), you can either: + +1. **Record once with a real error** (e.g., using an invalid API key or endpoint), then replay the error response +2. **Create a cassette manually** with a canned error response for testing + +Example: testing a `RateLimitError`: + +```python +@pytest.mark.vcr +def test_chat_openai_handles_rate_limit(): + """Test that rate limit errors are handled gracefully.""" + chat = ChatOpenAI(model="gpt-4o-mini") + try: + response = chat.invoke("Hello") + except Exception as e: + assert "rate" in str(e).lower() +``` + +The cassette for this test should contain a recorded 429 (Too Many Requests) response from the API. + +## Cassette Management + +### Recording New Cassettes + +To record a new cassette or re-record an existing one: + +```bash +cd libs/partners/openai + +# Record new cassettes (default --record-mode=once) +uv run --group test --group test_integration pytest \ + tests/integration_tests/chat_models/test_base.py::test_chat_openai_invoke + +# Or explicitly with --record-mode=new_episodes to overwrite +uv run --group test --group test_integration pytest \ + --record-mode=new_episodes \ + tests/integration_tests/chat_models/test_base.py::test_chat_openai_invoke +``` + +**Prerequisites**: +- A valid `OPENAI_API_KEY` in `tests/.env` +- Network access (obviously) + +**What happens**: +1. pytest-recording starts VCR in record mode +2. Your test runs and makes real HTTP calls to the API +3. VCR intercepts all requests and responses +4. Scrubbing functions redact sensitive headers and secrets +5. The cassette is serialized to YAML, compressed to `.yaml.gz`, and saved + +**Commit the cassette**: + +```bash +git add libs/partners/openai/tests/cassettes/test_chat_openai_invoke.yaml.gz +git commit -m "Add cassette for chat_openai_invoke integration test" +``` + +### Refreshing Stale Cassettes + +When test code changes (e.g., you change the prompt, the model, or the parameters), the cassette may become stale. VCR will attempt to match the new request body against the old cassette; if there's a mismatch, it will fail with a `CannotOverwriteExistingCassetteException` error (in playback mode) or suggest re-recording. + +To refresh: + +```bash +cd libs/partners/openai +uv run --group test --group test_integration pytest \ + --record-mode=new_episodes \ + tests/integration_tests/chat_models/test_base.py::test_chat_openai_invoke +``` + +This overwrites the cassette with the new interaction. Re-verify the cassette was scrubbed correctly, then commit. + +### Cassette Format + +Cassettes are YAML files (optionally gzip-compressed). A typical cassette structure: + +```yaml +interactions: + - request: + body: null + headers: {} + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: '{"choices": [{"message": {"content": "Hello!", "role": "assistant"}}]}' + headers: + content-type: + - application/json + status: + code: 200 + message: OK +version: 1 +``` + +Headers and secrets are replaced with `PLACEHOLDER` or `**REDACTED**`: + +```yaml +request: + headers: + authorization: + - "**REDACTED**" + openai-organization: + - "PLACEHOLDER" +``` + +### Inspecting Cassettes + +To inspect a cassette, decompress and read: + +```bash +gunzip -c libs/partners/openai/tests/cassettes/test_invoke.yaml.gz | head -50 +``` + +Or, for a permanent view: + +```bash +gunzip libs/partners/openai/tests/cassettes/test_invoke.yaml.gz +# Now test_invoke.yaml is readable +``` + +(Remember to re-compress or re-record before committing.) + +## Running Integration Tests + +### Full Integration Test Suite + +Run all integration tests against live APIs (requires credentials): + +```bash +cd libs/partners/openai +make integration_tests # Runs full suite with live API calls +``` + +This invokes: + +```bash +uv run --with 'openai>=2.45.0,<3.0.0' --group test --group test_integration pytest \ + -v --tb=short tests/integration_tests/ + +uv run --with 'openai>=3.0.0,<4.0.0' --group test --group test_integration pytest \ + -v --tb=short tests/integration_tests/ +``` + +It also smoke-tests a single live request on each supported OpenAI SDK major version (2.x and 3.x). + +### VCR Cassette Tests (CI Mode) + +Run integration tests using only cassettes (no live API calls, no credentials): + +```bash +cd libs/partners/openai +make test_vcr +``` + +This invokes: + +```bash +uv run --group test pytest --record-mode=none -m vcr tests/integration_tests/ +``` + +The `--record-mode=none` flag tells VCR to **only replay** cassettes and fail if a cassette is missing (never attempt a live call or record a new one). + +This is what CI runs; it ensures cassettes are up-to-date and tests are repeatable without exposing secrets. + +### Scheduled Integration Tests + +Scheduled CI workflows (daily or on-demand) run live integration tests with real credentials: + +```bash +cd libs/partners/openai +make integration_tests +``` + +Only tests marked `@pytest.mark.scheduled` run in these workflows. See [CI/CD Workflows](repo:///openwiki/ci-workflows.md#integration-test-compilation) for details. + +## Streaming and Async Integration Tests + +### Async Tests + +Async integration tests work the same as sync tests: + +```python +@pytest.mark.vcr +async def test_chat_openai_ainvoke(): + """Test async invoke.""" + chat = ChatOpenAI(model="gpt-4o-mini") + response = await chat.ainvoke("Hello") + assert response.content +``` + +VCR intercepts the underlying `httpx` (or `requests`) library, so both sync and async HTTP calls are recorded and replayed identically. + +### Streaming with `stream_events` + +Tests that use `stream_events` (the LangChain event streaming API) also work with cassettes: + +```python +@pytest.mark.vcr +def test_chat_openai_stream_events(): + """Test streaming events.""" + chat = ChatOpenAI(model="gpt-4o-mini") + events = [] + for event in chat.stream_events("Hello", version="v2"): + events.append(event) + assert len(events) > 0 +``` + +The underlying HTTP calls are recorded in the cassette; the event structure is replayed exactly. + +## VCR Configuration Details + +### conftest.py Setup + +Each package's `conftest.py` configures VCR globally: + +```python +from langchain_tests.conftest import CustomPersister, CustomSerializer, base_vcr_config + +@pytest.fixture(scope="session") +def vcr_config() -> dict: + """Extend the default configuration coming from langchain_tests.""" + config = base_vcr_config() + # Base config: + # - record_mode: "once" + # - filter_headers: ["authorization", "x-api-key", "api-key"] + # - match_on: ["method", "uri", "body"] + # - cassette_library_dir: "tests/cassettes" + + # Extend for OpenAI: + config["match_on"] = ["json_body"] # Custom JSON matching + config["serializer"] = "yaml.gz" # Compress cassettes + config["before_record_request"] = remove_request_headers + config["before_record_response"] = remove_response_headers + return config + +def pytest_recording_configure(config: dict, vcr: VCR) -> None: + """Register custom serializer, persister, and matchers.""" + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) + vcr.register_matcher("json_body", _json_body_matcher) +``` + +### Record Modes + +VCR supports several record modes: + +- **`once`** (default): Record new cassettes; replay existing ones. Use for initial development. +- **`new_episodes`**: Record/overwrite cassettes even if they exist. Use when refreshing stale cassettes. +- **`none`**: Never record; replay only. Use in CI. Fails if cassette is missing. +- **`all`**: Always record, even for existing cassettes. Use sparingly. + +### Request Matching + +By default, VCR matches requests by: + +```python +match_on: ["method", "uri", "body"] +``` + +This means: "A request matches a cassette if the HTTP method, URI, and body are identical." + +For APIs with non-deterministic bodies (e.g., timestamps in the request), you can customize matchers or use `allow_playback_repeats` to reuse the same response for multiple request variants. + +OpenAI tests use a custom `json_body` matcher: + +```python +config["match_on"] = ["json_body"] # Only match on JSON-parsed body (ignores whitespace/key order) +``` + +This allows cassettes to match even if JSON key order differs. + +## Integration with CI/CD + +### CI Workflows + +The LangChain CI system runs integration tests at multiple points: + +1. **Pull request checks** (`check_diffs.yml`): Compiles integration tests without running them +2. **VCR cassette tests** (`_test_vcr.yml`): Runs cassette-backed integration tests in playback-only mode +3. **Scheduled integration tests** (`integration_tests.yml`): Daily remote API testing with live credentials against partner libraries + +See [CI/CD Workflows: GitHub Actions and Release Process](repo:///openwiki/ci-workflows.md) for full details, including scheduled workflows and record mode management. + +### Cassette Validation in CI + +The `test_vcr` job runs with `--record-mode=none`, which means: + +- Cassettes are **never recorded** or overwritten in CI +- If a cassette is missing, the test **fails immediately** +- If request bodies don't match, the test **fails immediately** + +This catches stale cassettes caused by test code changes without attempting live calls. + +## Extension Points and Customization + +### Custom VCR Matchers + +For advanced matching logic (e.g., ignoring certain request fields), register a custom matcher in `conftest.py`: + +```python +def _custom_matcher(r1: Any, r2: Any) -> None: + """Match requests with custom logic.""" + # Example: match method and URI, ignore timestamps in body + assert r1.method == r2.method + assert r1.uri == r2.uri + # Don't check body + +@pytest.fixture(scope="session") +def vcr_config() -> dict: + config = base_vcr_config() + config["match_on"] = ["custom"] + return config + +def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_matcher("custom", _custom_matcher) +``` + +### Custom Scrubbers + +To add additional scrubbing for proprietary secrets: + +```python +def remove_proprietary_header(request: Any) -> Any: + """Remove custom header.""" + request.headers.pop("X-Proprietary-Token", None) + return request + +@pytest.fixture(scope="session") +def vcr_config() -> dict: + config = base_vcr_config() + config["before_record_request"] = remove_proprietary_header + return config +``` + +## Best Practices + +1. **Always scrub cassettes**: Use `before_record_request` and `before_record_response` to redact all sensitive data. Inspect cassettes before committing. + +2. **Commit cassettes**: Cassettes are part of your test suite. Commit them to git so others can run tests without live API access. + +3. **Keep cassettes small**: Avoid recording huge responses (e.g., embeddings of long documents). If needed, use manual cassette creation with canned data. + +4. **Use markers correctly**: + - `@pytest.mark.vcr` for cassette-backed tests + - `@pytest.mark.scheduled` for live-only tests + - No marker for offline tests + +5. **Test against multiple models/versions**: Use `@pytest.mark.parametrize` to test multiple configurations and generate separate cassettes for each. + +6. **Document cassette dependencies**: If a cassette depends on specific model behavior (e.g., a reasoning model), document it in the test. + +7. **Refresh cassettes when code changes**: If you modify the test (new prompt, new parameters), refresh the cassette with `--record-mode=new_episodes`. + +8. **Review cassette diffs**: When committing cassette updates, review the diff in your PR to ensure no secrets leaked. diff --git a/openwiki/mcp-integration.md b/openwiki/mcp-integration.md new file mode 100644 index 0000000000..479be62d87 --- /dev/null +++ b/openwiki/mcp-integration.md @@ -0,0 +1,444 @@ +--- +type: "Reference" +title: "Bearer token" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-6d1e3478d5b63988ee177552 + resource: repo://libs/langchain_v1/examples/mcp/auth_bearer.py + - id: openwiki-source-4bad19dc422af3ebb00e7f2f + resource: repo://libs/langchain_v1/examples/mcp/auth_oauth.py + - id: openwiki-source-46fd56b09fa62a41e3c41f08 + resource: repo://libs/langchain_v1/examples/mcp/destructive_interrupt.py + - id: openwiki-source-8b95e4b88972026f6c7678a3 + resource: repo://libs/langchain_v1/examples/mcp/graph_factory.py + - id: openwiki-source-cfb2965ed32b54e99ffb6328 + resource: repo://libs/langchain_v1/examples/mcp/multi_server.py + - id: openwiki-source-71f7ffcbb69cda81c2e3f940 + resource: repo://libs/langchain_v1/examples/mcp/protocol_eras.py + - id: openwiki-source-37b31519003157eb1b1bdaef + resource: repo://libs/langchain_v1/examples/mcp/README.md + - id: openwiki-source-6df781509d081d60a037331b + resource: repo://libs/langchain_v1/examples/mcp/tool_errors.py + - id: openwiki-source-caa1f747bb1ba9b6514eeaac + resource: repo://libs/langchain_v1/examples/mcp/transports.py + - id: openwiki-source-0a3228970b0eadc4bcadbb5d + resource: repo://libs/langchain_v1/langchain/mcp/adapter.py + - id: openwiki-source-b4c5eca79ce58abf486c2776 + resource: repo://libs/langchain_v1/langchain/mcp/elicitation.py + - id: openwiki-source-4715c337e9b93b9d00846133 + resource: repo://libs/langchain_v1/langchain/mcp/tools.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +The Model Context Protocol (MCP) is a standard for LLM applications to discover and call tools from external servers. `langchain.mcp` provides the `MCPAdapter` class, which discovers MCP tools and converts them to LangChain `BaseTool` objects suitable for use with `create_agent`. The adapter handles protocol negotiation via FastMCP, manages multiple transports (stdio, HTTP, in-memory), supports mid-call user input via LangGraph interrupts, and surfaces tool errors to the model for recovery and retry. + +## MCP Concept + +MCP is a request–response protocol where: + +- **Clients** (like LangChain agents) discover tools available on a server and invoke them with arguments +- **Servers** expose tools, describe their schemas, and handle calls +- **Tools** are named, documented functions with typed arguments; a server may expose many tools +- **Content** returned by a tool (text, images, files, structured data) is represented as content blocks + +MCP evolved between protocol eras: the 2025-11-25 era negotiates capability via an `initialize` handshake; the 2026-07-28 era uses a `server/discover` handshake. Both eras can coexist in an agent when separate adapters are used per server. + +## Architecture: MCPAdapter and FastMCP + +`MCPAdapter` is the user-facing entry point. It wraps one or more FastMCP clients and exposes their tools as LangChain tools: + +```python +async with MCPAdapter(target) as adapter: + tools = await adapter.list_tools() + agent = create_agent("anthropic:claude-sonnet-5", tools) +``` + +**Target types** (inferred by FastMCP): + +- `str` (http/https URL only) — reached over streamable HTTP +- `Path` — launched as a subprocess over stdio, e.g. `Path("server.py")` +- `FastMCP` — in-process server with no network or subprocess +- `Client` or `ClientGroup` — pre-built FastMCP client(s) +- `MCPConfig` (dict) — multiple servers, each with independent transport and auth +- `ClientTransport` — explicit transport (HTTP, stdio, or custom) + +String targets must be http(s) URLs to prevent silent local execution of existing `.py` or `.js` files. Local servers are accessed through `Path`, a transport, or `MCPConfig`. + +## Tool Discovery and Conversion + +`adapter.list_tools()` calls `fastmcp.Client.list_tools()` to fetch remote tools, then converts each via `as_langchain_tool()`: + +- **Discovery** uses the client's response cache (configurable via `cache_mode`) +- **Conversion** reads the MCP tool's schema (from `tool.input_schema`) and creates a `StructuredTool` with: + - `name`, `description`, and typed `args_schema` from the MCP definition + - `coroutine` that calls the tool asynchronously + - `response_format="content_and_artifact"` to return both model-visible content blocks and structured data + - `metadata["mcp"]` carrying tool annotations, server identity, and destructive hints + - `handle_tool_error` handler to surface MCP-reported errors (not transport failures) to the model + +For multi-server setups (via `ClientGroup` or `MCPConfig`), tool names are prefixed per server (e.g., `weather_forecast`, `calc_add`) to disambiguate tools with the same name on different servers. The adapter's internal routing ensures each call reaches the correct server. + +## Tool Invocation and Result Conversion + +When a tool is called: + +1. **Elicitation detection**: The adapter checks whether the underlying client is armed to drive LangGraph interrupts (see [Elicitation](#elicitation-input-mid-call) below) +2. **Tool call**: If interrupts are enabled, calls `_call_tool_with_interrupts()` to answer requests via `interrupt()`; otherwise calls `fastmcp.Client.call_tool()` directly +3. **Result conversion**: Converts MCP content blocks (text, images, files, resources) to LangChain content blocks +4. **Error handling**: If the server reports `isError=True`, raises `_MCPToolExecutionError` (a `ToolException`), which becomes a `ToolMessage` with `status="error"` so the model can see and retry +5. **Artifacts**: Extracts `structured_content` (JSON, tables, etc.) into a separate artifact field so the model receives both rendered content and structured data + +Content blocks support: +- **Text** — plain string +- **Image** — base64 or URL-referenced +- **File** — base64 or URL-referenced +- **Resource** — embedded binary or text, or URL link + +Audio is not yet supported. + +## Elicitation: Input Mid-Call + +Some MCP tools cannot complete without asking the user a question mid-call. Instead of hanging or erroring, the server returns an `InputRequiredResult` describing what it needs. The adapter converts this to a LangGraph `interrupt()`, so a human can answer and the run resumes seamlessly. + +**How it works:** + +1. **Arming**: On construction, `MCPAdapter` calls `_arm_for_interrupts()` on each underlying client, setting an elicitation callback. This advertises the `elicitation` capability to the server. A pre-built client that already has its own handler is cloned first, so the caller's object is never mutated. + +2. **Interrupt loop**: When `as_langchain_tool()` calls the tool, if the client was armed, it calls `_call_tool_with_interrupts()` instead of plain `call_tool()`. This loop: + - Issues the tool call with `allow_input_required=True` + - If the result is `InputRequiredResult`, extracts elicitation requests (form or URL) + - Raises `interrupt()` with the request, pausing the run + - On resume, receives answers keyed by request ID, builds response payloads, and retries the call + - Repeats until the tool returns a terminal result + +3. **Protocol era compatibility**: The interrupt loop only runs on modern servers (2026-07-28 and later) that return `InputRequiredResult`. Legacy servers never trigger it, so they work unchanged. A client armored with a pre-built handler uses that handler instead. + +**Request types:** + +- **Form** — server asks for structured data (JSON matching a schema); human must provide it +- **URL** — server asks the human to visit a URL (e.g., for approval or authentication); no data needed +- **Deny or cancel** — human can refuse a specific request (tool continues) or abort the call entirely + +## Transport Types + +Three main transports, selected automatically by FastMCP: + +### In-Memory + +A `FastMCP` server instance runs in the same process with no subprocess or network: + +```python +from langchain.mcp import MCPAdapter + +server = FastMCP("weather") +@server.tool +def get_forecast(city: str) -> str: + return f"{city}: sunny" + +async with MCPAdapter(server) as adapter: + tools = await adapter.list_tools() +``` + +**Ideal for**: tests, development, and single-app deployments with full control. + +### Stdio + +A script (Python or Node.js) is launched as a subprocess and communicates over stdin/stdout: + +```python +from pathlib import Path +from langchain.mcp import MCPAdapter + +script_path = Path("server.py") # must exist +async with MCPAdapter(script_path) as adapter: + tools = await adapter.list_tools() +``` + +**Ideal for**: local development, private tools, and sandboxing. Each adapter instance spawns one subprocess. + +### HTTP (Streamable) + +A remote MCP server is reached over HTTP(S) using a streaming transport: + +```python +from langchain.mcp import MCPAdapter + +url = "https://api.example.com/mcp" +async with MCPAdapter(url) as adapter: # no auth + tools = await adapter.list_tools() +``` + +**Ideal for**: public MCP servers (e.g., DeepWiki), cloud services, and third-party integrations. + +## Multi-Server Setup: Tool Prefixing + +To connect multiple MCP servers and expose all their tools to a single agent: + +```python +from langchain.mcp import MCPAdapter + +config = { + "mcpServers": { + "weather": {"command": "python", "args": ["weather_server.py"]}, + "calc": {"command": "python", "args": ["calc_server.py"]}, + } +} + +async with MCPAdapter(config) as adapter: + tools = await adapter.list_tools() # ["weather_forecast", "calc_add", ...] + agent = create_agent("anthropic:claude-sonnet-5", tools) +``` + +FastMCP automatically prefixes tools by config key (`weather_` + `forecast` = `weather_forecast`). This prevents collisions and makes tool provenance visible. The adapter's internal router ensures each call reaches the correct server. Servers can mix transports within one config: some stdio, some HTTP, some in-process. + +## Authentication + +MCP servers can require credentials. The adapter and client support: + +- **Bearer token** — static token, no discovery or refresh +- **OAuth 2.1** — full flow with dynamic client registration, browser redirect, and token exchange +- **Custom auth** — any `httpx2.Auth` implementation + +```python +from fastmcp.client import Client +from langchain.mcp import MCPAdapter + +# Bearer token +async with MCPAdapter(Client("https://api.example.com/mcp", auth="token-value")) as adapter: + tools = await adapter.list_tools() + +# OAuth (opens browser, auto-approves on demo server) +async with MCPAdapter(Client("https://api.example.com/mcp", auth="oauth")) as adapter: + tools = await adapter.list_tools() +``` + +For multi-server setups, specify auth per server in the `MCPConfig`: + +```python +config = { + "mcpServers": { + "api1": { + "command": "python", + "args": ["server.py"], + "auth": {"type": "bearer", "token": "secret-1"}, + }, + "api2": { + "command": "python", + "args": ["server.py"], + "auth": {"type": "oauth"}, + }, + } +} +``` + +## Metadata and Tool Annotations + +MCP tools can carry annotations (e.g., `destructiveHint=True` for deletion operations). These are surfaced on the LangChain tool as `metadata["mcp"]["tool"]["annotations"]`: + +```python +@server.tool(annotations=ToolAnnotations(destructiveHint=True)) +def delete_file(path: str) -> str: + return f"Deleted {path}" +``` + +Clients can read this to gate destructive tools behind approval without hardcoding tool names: + +```python +def _is_destructive(tool): + annotations = (tool.metadata or {}).get("mcp", {}).get("tool", {}).get("annotations", {}) + return annotations.get("destructive_hint", False) + +destructive_tools = [tool.name for tool in tools if _is_destructive(tool)] +# Pass to HumanInTheLoopMiddleware or similar approval gate +``` + +## Error Handling and Recovery + +**MCP tool errors** (when a server reports `isError=True`): + +- Converted to `ToolMessage` with `status="error"` and the server's message +- Visible to the model, which can correct inputs and retry +- Example: division by zero, file not found, network timeout at the remote server + +**Transport errors** (network, subprocess failure, malformed response): + +- Raised as exceptions; the run fails +- Models cannot act on these, so they should be retried at the orchestration level +- Example: unreachable URL, subprocess crashed, invalid JSON from server + +## Long-Lived Adapters: Graph Factory Pattern + +For per-request server setup (e.g., per-user credentials), create tools inside a graph factory: + +```python +async def make_graph(runtime): + user = runtime.user.identity + auth = BearerAuth(token_for(user)) + group = ClientGroup({ + "api1": Client("https://api.example.com/mcp", auth=auth), + "api2": Client("https://api.example.com/mcp", auth=auth), + }) + tools = await MCPAdapter(group).list_tools() + return create_agent("anthropic:claude-sonnet-5", tools) +``` + +For cross-run state (shared HTTP connection pool, response cache), instantiate outside the factory: + +```python +_pool = httpx2.AsyncHTTPTransport() +_cache = InMemoryResponseCacheStore() + +async def make_graph(runtime): + user = runtime.user.identity + group = ClientGroup({ + name: Client( + StreamableHttpTransport(url, httpx_client_factory=_client_factory), + cache=CacheConfig(store=_cache, partition=user), + ) + for name, url in SERVERS.items() + }) + tools = await MCPAdapter(group).list_tools(cache_mode="use") + return create_agent("anthropic:claude-sonnet-5", tools) +``` + +## Protocol Eras + +Two MCP protocol eras can coexist in one agent by using separate adapters per era: + +```python +# Legacy era server (2025-11-25, handshake-based) +legacy_client = Client(legacy_server(), mode="legacy") + +# Modern era server (2026-07-28, discovery-based) +modern_client = Client(modern_server(), mode="auto") + +async with MCPAdapter(legacy_client) as legacy_adapter, \ + MCPAdapter(modern_client) as modern_adapter: + tools = await legacy_adapter.list_tools() + await modern_adapter.list_tools() + agent = create_agent("anthropic:claude-sonnet-5", tools) +``` + +A single `MCPConfig` fleet negotiates one era across all its members: if one member only speaks the legacy era, the whole fleet drops to it. Separate adapters ensure each server keeps the best era its connection supports. + +## Examples + +LangChain ships runnable examples in `examples/mcp/`: + +| Example | Shows | Notes | +|---------|-------|-------| +| `transports.py` | in-memory, stdio, and HTTP transports | one adapter, three targets | +| `remote_server.py` | public MCP server (DeepWiki) | agent researches a GitHub repo | +| `multi_server.py` | `MCPConfig` fleet with tool prefixing | two stdio servers | +| `graph_factory.py` | per-user credentials in a `langgraph dev` graph | long-lived adapter, shared pool | +| `protocol_eras.py` | legacy and modern era servers together | separate adapters per era | +| `tool_errors.py` | tool failure and model recovery | agent retries on error | +| `elicitation.py` | server requesting user input mid-call | form elicitation and resume | +| `destructive_interrupt.py` | gating destructive tools | using `destructiveHint` metadata | +| `auth_bearer.py` | static bearer token | simple auth example | +| `auth_oauth.py` | OAuth 2.1 with dynamic client registration | full flow, browser redirect | + +Run examples with: + +```bash +uv sync --extra mcp --extra anthropic +export ANTHROPIC_API_KEY=... +uv run examples/mcp/transports.py +``` + +## Integration Points + +### `create_agent` + +Tools from `MCPAdapter.list_tools()` pass directly to `create_agent()`, which routes tool calls through the agent's model and executor. Tools remain callable after the adapter context exits because they hold a reference to the underlying client. + +### LangGraph Checkpointer + +Elicitation-driven interrupts require a checkpointer so the run can pause and resume: + +```python +from langgraph.checkpoint.memory import InMemorySaver + +agent = create_agent( + "anthropic:claude-sonnet-5", + tools, + checkpointer=InMemorySaver(), +) +config = {"configurable": {"thread_id": "user-1"}} +paused = await agent.ainvoke({"messages": [...]}, config) +# Human answers interrupt; resume with command +resumed = await agent.ainvoke(Command(resume={...}), config) +``` + +### Tool Middleware + +Agents can apply middleware to gate or log tool calls. MCP tool metadata (e.g., `destructiveHint`) integrates with `HumanInTheLoopMiddleware`: + +```python +from langchain.agents.middleware import HumanInTheLoopMiddleware + +interrupt_on = { + tool.name: InterruptOnConfig(...) + for tool in tools + if _is_destructive(tool) +} +agent = create_agent(..., middleware=[HumanInTheLoopMiddleware(interrupt_on=interrupt_on)]) +``` + +## Configuration and Operations + +### Response Cache + +FastMCP caches tool lists and supports per-principal isolation. The adapter's `cache_mode` parameter controls cache use: + +- `"use"` (default) — serve from cache if fresh +- `"refresh"` — refresh from server, repopulate cache +- `"bypass"` — skip cache entirely + +```python +tools = await adapter.list_tools(cache_mode="refresh") +``` + +For long-lived adapters, configure the cache on the client to persist across runs: + +```python +cache = CacheConfig( + store=InMemoryResponseCacheStore(), + target_id="user-id", + partition="user-partition" +) +client = Client(url, cache=cache) +tools = await MCPAdapter(client).list_tools(cache_mode="use") +``` + +### Logging and Observability + +`MCPAdapter` and `as_langchain_tool()` are transparent to LangChain's logging and observability hooks. Tool calls are logged as `ToolMessage` events in the agent's message history. Elicitation interrupts and responses are visible in the run's state transitions. + +## Invariants and Failure Semantics + +- **Tool availability**: Once `list_tools()` completes, tools remain callable even after the adapter context exits (they hold the client) +- **Elicitation re-run**: When a tool is resumed with an answer, it is called again from the start. A server that works first and asks after repeats that work once per round +- **Error propagation**: Transport errors propagate as exceptions; MCP tool errors (isError=True) become model-visible `ToolMessage` errors +- **Client reuse**: Clients are reentrant; a tool can open its client even if a connection is already held elsewhere +- **Pre-built client cloning**: If a caller passes a client with an existing elicitation handler, it is cloned so the caller's object is never mutated +- **Group naming**: Tools from a `ClientGroup` are prefixed by config key; the router resolves each call to the correct member +- **No concurrent elicitation**: Elicitation answers are driven sequentially, one `interrupt()` per round, so LangGraph can match resume values by order + +## Extension Points + +- **Custom transport**: Pass any `fastmcp.ClientTransport` to support non-standard protocols +- **Custom auth**: Implement `httpx2.Auth` for authentication schemes beyond bearer token and OAuth +- **Custom metadata handler**: Subclass `StructuredTool` to customize how MCP metadata is exposed on the LangChain tool +- **Custom error handler**: Override `_handle_mcp_tool_error()` or provide your own `handle_tool_error` to the tool +- **Custom interruption**: Provide a pre-built client with your own `elicitation_handler` to override the interrupt-driven default + +## Related Pages + +- **[tools.md](/openwiki/tools.md)** — LangChain tool abstractions, `BaseTool`, `StructuredTool` +- **[agent-execution.md](/openwiki/agent-execution.md)** — agent orchestration, `create_agent`, tool routing diff --git a/openwiki/messages.md b/openwiki/messages.md new file mode 100644 index 0000000000..b8f01a8abe --- /dev/null +++ b/openwiki/messages.md @@ -0,0 +1,499 @@ +--- +type: "Architecture" +title: "Message Types and Content Representation" +description: "Document the message abstraction, standardized content blocks for multimodal LLM I/O, message hierarchy, and provider-specific block translators." +tags: [messages, content-blocks, chat-models, streaming, multimodal, provider-adapters] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-77dc1fb726463969f9d53658 + resource: repo://libs/core/langchain_core/messages/ai.py + - id: openwiki-source-b32b84365d17276620c41ebc + resource: repo://libs/core/langchain_core/messages/base.py + - id: openwiki-source-2e77747f30fe980d17d5d1a2 + resource: repo://libs/core/langchain_core/messages/block_translators/__init__.py + - id: openwiki-source-b0f0ae0889f60e428f2f1b96 + resource: repo://libs/core/langchain_core/messages/block_translators/anthropic.py + - id: openwiki-source-ad04883edeb0ba80d9ebcb7e + resource: repo://libs/core/langchain_core/messages/block_translators/langchain_v0.py + - id: openwiki-source-ac2e0f8b0fb1cb3b223672b7 + resource: repo://libs/core/langchain_core/messages/block_translators/openai.py + - id: openwiki-source-fc874ddb29b9c5840565397f + resource: repo://libs/core/langchain_core/messages/content.py + - id: openwiki-source-8bb392f5dbc1fe7faaf52430 + resource: repo://libs/core/langchain_core/messages/human.py + - id: openwiki-source-dad8cfeb38a829e03e165986 + resource: repo://libs/core/langchain_core/messages/system.py + - id: openwiki-source-9861ba5cf0c42c142cf732f9 + resource: repo://libs/core/langchain_core/messages/tool.py + - id: openwiki-source-498a9586e021b126ab8a8b42 + resource: repo://libs/core/langchain_core/messages/utils.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +LangChain's message abstraction provides a unified, provider-agnostic interface for representing conversational inputs and outputs to large language models. At its core is **`BaseMessage`**, a serializable container for content that can hold either plain text strings or a structured list of **content blocks**—TypedDict objects representing text, images, audio, video, tool calls, reasoning, and more. + +The key innovation is **content blocks**: instead of provider-specific schemas (OpenAI's `image_url` vs. Anthropic's `document` source blocks), LangChain normalizes all content into a unified format. This allows applications to work with multimodal messages portably, with adapters (block translators) converting to provider-specific formats only at invocation time. + +## BaseMessage Hierarchy and Core Fields + +**Location**: `repo://libs/core/langchain_core/messages/base.py#L93-L180` + +`BaseMessage` is the abstract base for all message types. Key fields: + +- **`content`**: `str | list[str | dict[Any, Any]]` + Holds either plain text or a mixed list of strings (treated as text blocks) and dictionaries (content block dicts). + +- **`type`**: `str` (field required by schema) + Uniquely identifies the message kind (`"human"`, `"ai"`, `"system"`, `"tool"`, `"chat"`, `"function"`, or chunk variants). + +- **`additional_kwargs`**: `dict[Any, Any]` + Reserved for provider-specific data not yet mapped to standard fields (e.g., `reasoning_content` from Ollama or DeepSeek). + +- **`response_metadata`**: `dict[Any, Any]` + Metadata about the response: headers, token counts, model name, provider name, output version. + +- **`name`** (optional): Human-readable identifier for the message; unused by most models. + +- **`id`** (optional): Unique identifier, typically assigned by the model provider. + +### Core Message Types + +**`HumanMessage`** (`repo://libs/core/langchain_core/messages/human.py#L9-L61`) +Represents user input. Used for prompts, questions, and conversation turns from the user. Has chunk variant `HumanMessageChunk` for streaming support. + +**`AIMessage`** (`repo://libs/core/langchain_core/messages/ai.py#L160-L305`) +Represents model output. Contains specialized fields: +- **`tool_calls`**: List of `ToolCall` dicts (structured tool invocation requests). +- **`invalid_tool_calls`**: `ToolCall` dicts that failed parsing (malformed JSON args, etc.). +- **`usage_metadata`**: `UsageMetadata` dict with standardized token counts (`input_tokens`, `output_tokens`, `total_tokens`, plus optional per-category breakdowns). + +The `AIMessageChunk` variant is used when streaming and holds `tool_call_chunks` instead of complete `tool_calls`. + +**`SystemMessage`** (`repo://libs/core/langchain_core/messages/system.py#L9-L61`) +Primes model behavior; typically the first message in a conversation. Also supports chunks via `SystemMessageChunk`. + +**`ToolMessage`** (`repo://libs/core/langchain_core/messages/tool.py#L26-L164`) +Represents the result of a tool invocation. Required fields: +- **`tool_call_id`**: Links this result to the `AIMessage.tool_calls[].id` that requested it. +- **`content`**: The tool's output (string or list of content blocks). +- **`status`**: `"success"` or `"error"`. +- **`artifact`** (optional): Full tool output not sent to model (e.g., raw data when only a summary is in `content`). + +**`ChatMessage`** and **`FunctionMessage`** +Legacy/specialized message types. `ChatMessage` is generic with a `role` field; `FunctionMessage` represents deprecated function-calling format. + +### Message Chunks and Streaming + +**Location**: `repo://libs/core/langchain_core/messages/base.py#L450-L500` (BaseMessageChunk) + +During streaming, models emit `AIMessageChunk` objects incrementally. These chunks are designed to be **mergeable**: when combined with `+`, they accumulate content, merge tool call chunks by index, and aggregate token usage. + +**`AIMessageChunk`** fields: +- **`tool_call_chunks`**: Partial tool call objects with nullable `name` and `args` (JSON string fragments). +- **`chunk_position`**: `"last"` on the final chunk, signaling aggregation triggers (e.g., parsing completed tool call chunks into full `tool_calls`). + +Merging chunks with `+` invokes `add_ai_message_chunks()`, which: +- Concatenates string content and merges list content blocks. +- Combines `tool_call_chunks`, respecting their `index` field. +- Aggregates token usage across chunks. +- On the final chunk (`chunk_position="last"`), parses accumulated tool call chunks into complete `ToolCall` objects. + +## Content Blocks: Unified Multimodal Representation + +**Location**: `repo://libs/core/langchain_core/messages/content.py#L1-L878` + +Content blocks are **TypedDict objects** representing distinct types of message content. They provide a provider-agnostic abstraction that block translators convert to provider formats. + +### Standard Block Types + +**TextContentBlock** +```python +{ + "type": "text", + "text": str, + "id": str (optional, auto-generated), + "annotations": list[Annotation] (optional, citations/metadata), + "index": int | str (optional, for streaming), + "extras": dict (optional, provider-specific fields), +} +``` +Plain text output from a model. Annotations enable citations pointing to source documents. + +**ReasoningContentBlock** +```python +{ + "type": "reasoning", + "reasoning": str (optional), + "id": str (optional), + "index": int | str (optional), + "extras": dict (optional), +} +``` +Chain-of-thought or intermediate reasoning from models like o1, o3, etc. Often extracted from `` tags or provider-specific fields in `additional_kwargs`. + +**ToolCall** +```python +{ + "type": "tool_call", + "id": str | None, + "name": str, + "args": dict, + "index": int | str (optional), + "extras": dict (optional), +} +``` +A request from the model to invoke a tool. ID must be unique per message to match with `ToolMessage` responses. + +**ToolCallChunk** (streaming variant) +```python +{ + "type": "tool_call_chunk", + "id": str | None, + "name": str | None, + "args": str | None, + "index": int | str (optional), + "extras": dict (optional), +} +``` +Partial tool call (emitted when streaming). String `args` accumulates JSON. Chunks with the same `index` are merged on arrival. + +**InvalidToolCall** +```python +{ + "type": "invalid_tool_call", + "id": str | None, + "name": str | None, + "args": str | None, + "error": str | None, + "index": int | str (optional), + "extras": dict (optional), +} +``` +Tool call that failed parsing. Error field captures the exception message. + +### Multimodal Data Blocks + +**ImageContentBlock** +```python +{ + "type": "image", + "url": str (optional), + "base64": str (optional), + "file_id": str (optional), + "mime_type": str (optional, required for base64), + "id": str (optional), + "index": int | str (optional), + "extras": dict (optional), +} +``` +Image data via URL, base64 encoding, or cloud file reference (e.g., OpenAI Files API). + +**AudioContentBlock**, **VideoContentBlock** +Similar structure to `ImageContentBlock` with `type` fields `"audio"` and `"video"`. + +**FileContentBlock** +```python +{ + "type": "file", + "url": str (optional), + "base64": str (optional), + "file_id": str (optional), + "mime_type": str (optional), + "id": str (optional), + "index": int | str (optional), + "extras": dict (optional), +} +``` +Generic file data (PDFs, Word docs, etc.) not covered by image/audio/plaintext types. + +**PlainTextContentBlock** +```python +{ + "type": "text-plain", + "text": str (optional), + "base64": str (optional), + "url": str (optional), + "file_id": str (optional), + "mime_type": Literal["text/plain"], + "title": str (optional), + "context": str (optional), + "id": str (optional), + "index": int | str (optional), + "extras": dict (optional), +} +``` +Plain text documents with optional title and context for model interpretation. + +### Server-Side Tool Calls + +**ServerToolCall**, **ServerToolCallChunk**, **ServerToolResult** +Support tool execution that happens server-side (e.g., code execution, web search). Models emit these to request execution without local handler code. + +### NonStandardContentBlock + +```python +{ + "type": "non_standard", + "value": dict, + "id": str (optional), + "index": int | str (optional), +} +``` +Holds provider-specific content that doesn't map to standard block types. Block translators attempt to parse non-standard blocks during the `content_blocks` property evaluation. + +### Accessing Content Blocks + +**Location**: `repo://libs/core/langchain_core/messages/base.py#L199-L260` + +The `content_blocks` property normalizes message content to a list of typed content block dicts: + +```python +@property +def content_blocks(self) -> list[types.ContentBlock]: +``` + +**Behavior**: +1. If `content` is a string, wrap it as `{"type": "text", "text": content}`. +2. Parse list items: strings become text blocks, dicts with known `type` values are kept as-is, others become `{"type": "non_standard", "value": ...}`. +3. For `AIMessage`, check `response_metadata["model_provider"]` and use the provider's translator if registered (e.g., OpenAI, Anthropic). +4. Fall back to best-effort parsing if no translator exists. +5. For `AIMessage`, append `tool_calls` not already in content as tool call blocks. +6. Extract reasoning from `additional_kwargs["reasoning_content"]` if present. + +## Block Translators: Adapting to Provider Formats + +**Location**: `repo://libs/core/langchain_core/messages/block_translators/__init__.py` and provider modules + +Block translators convert between LangChain's standard blocks and provider-specific formats. Each provider module registers translator functions that are invoked when accessing `AIMessage.content_blocks` if `response_metadata["model_provider"]` matches. + +### Registration System + +**`register_translator`** and **`get_translator`**: +```python +def register_translator( + provider: str, + translate_content: Callable[[AIMessage], list[ContentBlock]], + translate_content_chunk: Callable[[AIMessageChunk], list[ContentBlock]], +) -> None +``` + +Translators are stored in `PROVIDER_TRANSLATORS` and auto-initialized on module load via `_register_translators()`. + +### Key Translators + +**OpenAI** (`repo://libs/core/langchain_core/messages/block_translators/openai.py`) +Handles Chat Completions format: +- Converts OpenAI's `image_url` blocks to standard `ImageContentBlock`. +- Parses `tool_calls` (from function calling) into `ToolCall` blocks. +- Supports Responses API with `input_audio`, `input_file`, and `input_image` types. +- `convert_to_openai_image_block()` and `convert_to_openai_data_block()` are public utilities used by models and integrations. + +**Anthropic** (`repo://libs/core/langchain_core/messages/block_translators/anthropic.py`) +Handles Anthropic's format: +- Converts `document` blocks (with `source` field specifying type: `base64`, `url`, `file`, or `text`) to standard file/plaintext blocks. +- Converts `image` blocks with various source types to `ImageContentBlock`. +- Populates `extras` with provider-specific fields like `cache_control`. + +**Google GenAI** and **Bedrock Converse** +Similar conversion logic for Google and AWS formats. + +**LangChain v0 (Backward Compatibility)** (`repo://libs/core/langchain_core/messages/block_translators/langchain_v0.py`) +Parses legacy `source_type`-based blocks (e.g., `{"type": "image", "source_type": "url", "url": "..."}`) into v1 blocks. + +### Translation Flow in `content_blocks` + +When `AIMessage.content_blocks` is accessed: +1. Check `response_metadata["output_version"]` for `"v1"` (already normalized, short-circuit). +2. Attempt provider-specific translation if `response_metadata["model_provider"]` is set. +3. Fall back to `BaseMessage.content_blocks` best-effort parsing. +4. For `AIMessage` specifically, append any tool calls not already in content and extract reasoning from kwargs. + +## Message Manipulation Utilities + +**Location**: `repo://libs/core/langchain_core/messages/utils.py#L1-L150` and beyond + +The utils module provides helpers for working with messages: + +**`get_buffer_string`** +Converts a sequence of messages to a single string for logging/debugging. Supports `format="prefix"` (role-prefixed) or `format="xml"` (XML tags with proper escaping). Multimodal content blocks are rendered with safe truncation and base64-encoded data omitted. + +**`convert_to_messages` and `convert_to_openai_messages`** +Coerce various input formats (dicts, strings, `MessageLikeRepresentation` union) into typed message objects. + +**`filter_messages`, `trim_messages`, `merge_message_runs`** +Filter, truncate, and deduplicate consecutive messages of the same type. + +**`AnyMessage` Union Type** +```python +AnyMessage = Annotated[ + Annotated[AIMessage, Tag(tag="ai")] + | Annotated[HumanMessage, Tag(tag="human")] + | ... (all message and chunk types) + Field(discriminator=Discriminator(_get_type)), +] +``` +A tagged union for Pydantic deserialization. The `type` field discriminates the correct message class during deserialization. + +## Content Representation: String vs. Block List + +Messages accept content in two forms: + +**String content**: +```python +AIMessage(content="Hello, world!") +``` +Simple, backward compatible. Treated internally as a single text block. + +**Block list content**: +```python +AIMessage( + content=[ + {"type": "text", "text": "What is this?"}, + {"type": "image", "url": "https://example.com/img.png"}, + ] +) +``` +or using the typed `content_blocks` kwarg: +```python +AIMessage( + content_blocks=[ + create_text_block("What is this?"), + create_image_block(url="https://example.com/img.png"), + ] +) +``` + +The `text` property extracts all text blocks: +```python +msg = AIMessage(content=[ + {"type": "text", "text": "Hello"}, + {"type": "image", "url": "..."}, +]) +print(msg.text) # "Hello" +``` + +## Integration with Chat Models + +Chat models normalize message input and output using the message abstraction: + +- **Input**: Users provide messages (strings, dicts, or `MessageLikeRepresentation`). Models invoke `_normalize_messages()` to convert to `BaseMessage` objects and optionally expand multimodal content for the target provider. + +- **Output**: Models return `AIMessage` with: + - `content`: Model's text response (or list of blocks if multimodal). + - `response_metadata`: Populated with `model_provider`, `output_version`, token counts, etc. + - `tool_calls`: Parsed from provider format into structured `ToolCall` dicts. + - `usage_metadata`: Standardized token counts. + +See `/openwiki/chat-models.md` for details on model invocation and streaming lifecycle. + +## Provider-Specific Extensions + +The `extras` field in content blocks allows provider metadata without breaking standard structure: + +```python +{ + "type": "text", + "text": "Response text", + "extras": { + "thought_signature": "EpoWCpc...", # Google + "cache_control": {"type": "ephemeral"}, # Anthropic + }, +} +``` + +This approach maintains type safety while supporting emerging provider capabilities. + +## Backward Compatibility and Versioning + +LangChain v1.0 introduced the v1 content block format, superseding the v0 `source_type` style. The block translators handle both: + +- **v0 blocks** (e.g., `{"type": "image", "source_type": "url", "url": "..."}`) are recognized and wrapped as non-standard blocks, then parsed by `_convert_v0_multimodal_input_to_v1()`. +- **Provider-specific blocks** (e.g., OpenAI's `image_url` from raw API responses) are unpacked by provider translators. +- **Output version tracking**: `response_metadata["output_version"] = "v1"` signals that content is already normalized, allowing short-circuit optimization. + +## Example Workflows + +### Sending a Multimodal Message + +```python +from langchain_core.messages import HumanMessage, create_text_block, create_image_block + +message = HumanMessage( + content_blocks=[ + create_text_block("Describe this chart."), + create_image_block(url="https://example.com/chart.png", mime_type="image/png"), + ] +) + +# Access text +print(message.text) # "Describe this chart." + +# Get normalized blocks +for block in message.content_blocks: + print(block["type"]) # "text", "image" +``` + +### Handling Tool Calls from a Model + +```python +ai_msg = model.invoke([...]) +# ai_msg.tool_calls = [ +# {"type": "tool_call", "id": "call_1", "name": "search", "args": {"query": "..."}}, +# ] + +for tool_call in ai_msg.tool_calls: + result = invoke_tool(tool_call["name"], tool_call["args"]) + tool_response = ToolMessage( + content=str(result), + tool_call_id=tool_call["id"], + ) +``` + +### Streaming and Chunk Aggregation + +```python +chunks = [] +for chunk in model.stream(input_msg): + chunks.append(chunk) + print(f"Received: {chunk.content}") + +# Aggregate all chunks +final = chunks[0] +for chunk in chunks[1:]: + final = final + chunk + +# final.tool_calls are now complete (parsed from tool_call_chunks) +``` + +### Using Block Translators + +Block translators are invoked transparently when a model sets `response_metadata["model_provider"]`: + +```python +# OpenAI model +ai_msg = openai_model.invoke(msg) +# response_metadata contains model_provider="openai" + +blocks = ai_msg.content_blocks +# If content is from OpenAI's API, translator converts image_url → ImageContentBlock +``` + +Custom provider integrations can register their own translator: + +```python +from langchain_core.messages.block_translators import register_translator + +def my_translate_content(msg: AIMessage) -> list[ContentBlock]: + # Custom logic + pass + +def my_translate_content_chunk(chunk: AIMessageChunk) -> list[ContentBlock]: + # Custom logic + pass + +register_translator("my_provider", my_translate_content, my_translate_content_chunk) +``` diff --git a/openwiki/middleware.md b/openwiki/middleware.md new file mode 100644 index 0000000000..c4a561f809 --- /dev/null +++ b/openwiki/middleware.md @@ -0,0 +1,493 @@ +--- +type: "Reference" +title: "Middleware" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-bc95cc8fff4e07f74e50ce8b + resource: repo://libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py + - id: openwiki-source-c29f7722b0a4bcc0d760335e + resource: repo://libs/langchain_v1/langchain/agents/middleware/model_retry.py + - id: openwiki-source-219798683681ccdced950f4d + resource: repo://libs/langchain_v1/langchain/agents/middleware/tool_error.py + - id: openwiki-source-03e8ca0eebe37feda8566793 + resource: repo://libs/langchain_v1/langchain/agents/middleware/types.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +Middleware in LangChain agents provides a composable, layered approach to intercepting and modifying agent behavior without changing core agent logic. Middleware hooks into the agent execution loop to implement cross-cutting concerns: automatic retries, error handling, human-in-the-loop approval, PII redaction, structured output transformation, and tool caching. + +### Core Architectural Principles + +**Interception Model**: Each middleware instance registers sync/async handler pairs for lifecycle hooks. When the agent executes, hooks are invoked in registration order, with each middleware observing or transforming state before delegating to the next layer or the core execution logic. + +**Composition Semantics**: Middleware registered first in the list becomes the outermost layer. When composing `wrap_model_call` or `wrap_tool_call` handlers, each middleware wraps the previous ones, establishing a chain where the first middleware intercepts first and last. + +- **Model call stack example**: `[Middleware1, Middleware2, Middleware3]` becomes `Middleware1(Middleware2(Middleware3(model)))`, so `Middleware1` receives control first, calls the next handler (which is Middleware2's wrapper), receives the result, and can transform it before returning. +- **Tool call stack**: Identical composition order — first middleware is outermost. + +**State and Command Flow**: Most hooks return optional state updates (`dict[str, Any]`). The `wrap_model_call` and `wrap_tool_call` interception points also support returning `Command` objects from LangGraph, allowing middleware to redirect execution or jump to different nodes (e.g., skip tool execution, loop back to model, or exit). + +## AgentMiddleware Protocol + +All middleware classes inherit from `AgentMiddleware`, a generic base that defines optional hooks and establishes the contract for interceptors. + +### Type Parameters + +```python +class AgentMiddleware(Generic[StateT, ContextT, ResponseT]): + """ + StateT: Type of agent state (default: AgentState[Any]). + ContextT: Type of runtime context (default: None). + ResponseT: Type of structured response (default: Any). + """ +``` + +### Lifecycle Hooks + +Middleware can implement any or all of these lifecycle methods; unimplemented methods default to no-op: + +#### Before/After Agent + +```python +def before_agent(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Runs once at the very start of agent execution, before the first model call.""" + +async def abefore_agent(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Async version of before_agent.""" + +def after_agent(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Runs once after the agent loop terminates (no more tool calls or explicit exit).""" + +async def aafter_agent(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Async version of after_agent.""" +``` + +#### Before/After Model + +```python +def before_model(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Runs before each model invocation; allows state transformation before the call.""" + +async def abefore_model(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Async version of before_model.""" + +def after_model(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Runs after each model response; allows approval, modification, or rejection of tool calls.""" + +async def aafter_model(self, state: StateT, runtime: Runtime[ContextT]) -> dict[str, Any] | None: + """Async version of after_model.""" +``` + +#### Wrap Model Call (Core Interception) + +```python +def wrap_model_call( + self, + request: ModelRequest[ContextT], + handler: Callable[[ModelRequest[ContextT]], ModelResponse[ResponseT]], +) -> ModelResponse[ResponseT] | AIMessage | ExtendedModelResponse[ResponseT]: + """Intercept and control model execution. + + The handler callback executes the model call and returns ModelResponse. + Middleware can: + - Call handler once for normal execution. + - Call handler multiple times for retry logic. + - Skip calling handler to short-circuit (return a cached result). + - Modify request before calling handler. + - Transform response after handler returns. + + Returns ModelResponse, AIMessage (auto-wrapped), or ExtendedModelResponse (with Command). + """ + +async def awrap_model_call( + self, + request: ModelRequest[ContextT], + handler: Callable[[ModelRequest[ContextT]], Awaitable[ModelResponse[ResponseT]]], +) -> ModelResponse[ResponseT] | AIMessage | ExtendedModelResponse[ResponseT]: + """Async version of wrap_model_call.""" +``` + +#### Wrap Tool Call (Tool Interception) + +```python +def wrap_tool_call( + self, + request: ToolCallRequest, + handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]], +) -> ToolMessage | Command[Any]: + """Intercept tool execution. + + Called once per tool call with the full ToolCallRequest (tool name, args, call id, state, runtime). + Middleware can: + - Call handler for normal execution. + - Call handler multiple times for retries. + - Skip handler to return a cached result or mock. + - Modify tool args before handler. + - Convert exceptions to error ToolMessages. + + Returns ToolMessage or Command. + """ + +async def awrap_tool_call( + self, + request: ToolCallRequest, + handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]], +) -> ToolMessage | Command[Any]: + """Async version of wrap_tool_call.""" +``` + +### Configuration + +```python +class AgentMiddleware(Generic[StateT, ContextT, ResponseT]): + state_schema: type[StateT] = AgentState[Any] + """Optional custom state schema. Merged with base AgentState during graph compilation.""" + + tools: Sequence[BaseTool] = () + """Additional tools registered by this middleware (e.g., ShellToolMiddleware adds shell_tool).""" + + trace_policy: TracePolicy | None = None + """Optional trace policy controlling what is captured in spans for this middleware's hooks.""" + + transformers: Sequence[TransformerFactory] = () + """Stream transformer factories for streaming customization.""" +``` + +## Core Middleware Types and Patterns + +### Model Execution Middleware + +**ModelRetryMiddleware**: Automatically retries failed model calls with exponential backoff when transient errors (rate limits, API timeouts) occur. Supports custom exception filtering, custom failure handlers, and jitter to avoid thundering herd. + +```python +from langchain.agents.middleware import ModelRetryMiddleware + +retry = ModelRetryMiddleware( + max_retries=3, + retry_on=(RateLimitError, APITimeoutError), + backoff_factor=2.0, + initial_delay=1.0, + on_failure="continue", # or "error" to re-raise, or callable +) +``` + +**ModelFallbackMiddleware**: Wraps the primary model with a fallback model (typically smaller/faster/cheaper) when the primary fails or to diversify model selection strategies. + +**ModelCallLimitMiddleware**: Enforces a maximum number of model calls per agent invocation, preventing runaway loops. + +### Tool Execution Middleware + +**ToolErrorMiddleware**: Converts tool execution exceptions (e.g., API errors, validation failures) into error `ToolMessage` objects sent back to the model, allowing the model to recover or clarify. Opt-in by exception type. + +```python +from langchain.agents.middleware import ToolErrorMiddleware + +def on_error(exc: Exception, request: ToolCallRequest) -> str | None: + if isinstance(exc, ValueError): + return f"Invalid argument for {request.tool_call['name']}: {exc}" + return None # Propagate other exceptions + +middleware = ToolErrorMiddleware(on_error=on_error) +``` + +**ToolRetryMiddleware**: Retries failed tool calls with configurable backoff and validation logic. + +**ToolCallLimitMiddleware**: Prevents infinite tool loops by enforcing a maximum number of tool calls. + +### Control Flow & Approval Middleware + +**HumanInTheLoopMiddleware**: Pauses after model-requested tool calls and sends an interrupt with action summaries to a human reviewer. Supports approval, editing, rejection, or human-answered "respond" decision types. Tool calls are modified based on human feedback before execution. + +```python +from langchain.agents.middleware import HumanInTheLoopMiddleware + +hitl = HumanInTheLoopMiddleware( + interrupt_on={ + "delete_file": True, # All decision types allowed + "search": { + "allowed_decisions": ["approve", "reject"], + "description": "Searching online databases", + }, + } +) +``` + +### Data Transformation & Privacy Middleware + +**PIIMiddleware**: Detects personally identifiable information (PII) in prompts, model responses, and tool outputs; optionally redacts or transforms it. Supports configurable detectors and redaction rules. + +**ContextEditingMiddleware**: Allows dynamic modification of agent context (system message, available tools) during execution. + +**FileSearchMiddleware**: Integrates file search capabilities into the agent, retrieving relevant documents before model calls. + +### System Tools Middleware + +**ShellToolMiddleware**: Provides controlled shell command execution with configurable resource limits, sandboxing (host, Docker, Codex), timeout enforcement, and output truncation. + +**TodoListMiddleware**: Adds persistent todo list management capability. + +## Request and Response Types + +### ModelRequest + +```python +@dataclass +class ModelRequest(Generic[ContextT]): + """Request context for model execution.""" + + model: BaseChatModel # The model to invoke + messages: list[AnyMessage] # Messages (excluding system message) + system_message: SystemMessage | None # System instructions + tool_choice: Any # Tool selection strategy + tools: list[BaseTool | dict] # Available tools + response_format: ResponseFormat[Any] | None # Structured output format + state: AgentState[Any] # Full agent state + runtime: Runtime[ContextT] # Runtime context + model_settings: dict[str, Any] # Additional model parameters +``` + +**Immutable Pattern**: Middleware should not mutate `ModelRequest` directly. Use `request.override(**changes)` to create a new request with modifications. + +### ModelResponse + +```python +@dataclass +class ModelResponse(Generic[ResponseT]): + """Successful model execution result.""" + + result: list[BaseMessage] # Messages (typically one AIMessage) + structured_response: ResponseT | None = None # Parsed structured output +``` + +### ExtendedModelResponse + +```python +@dataclass +class ExtendedModelResponse(Generic[ResponseT]): + """Model response with optional LangGraph Command for additional state updates.""" + + model_response: ModelResponse[ResponseT] + command: Command[Any] | None = None +``` + +Middleware can return `ExtendedModelResponse` to apply a command that modifies state after the model node completes. Commands are applied through state reducers, so messages in commands are **added** to existing messages (not replaced). + +### ToolCallRequest + +```python +@dataclass +class ToolCallRequest: + """Request context for tool execution.""" + + tool_call: dict # Tool call dict with 'id', 'name', 'args' + tool: BaseTool | None # Resolved BaseTool instance (or None in batch mode) + state: AgentState[Any] # Agent state at time of call + runtime: Runtime[ContextT] # Runtime context (ToolRuntime with tool-specific info) +``` + +## Composition and Execution Order + +### Middleware Stack Execution + +When middleware is registered as `[M1, M2, M3]`: + +**Model Call Stack**: +1. M1's `wrap_model_call` is entered first +2. M1 calls handler → M2's `wrap_model_call` is entered +3. M2 calls handler → M3's `wrap_model_call` is entered +4. M3 calls handler → actual model execution +5. M3 returns result to M2 +6. M2 can transform result and returns to M1 +7. M1 can transform result and returns to agent + +**Result**: Innermost middleware (M3, closest to model) executes first; outermost (M1) sees and can override all inner results. + +### State Updates and Commands + +State updates from hooks are merged using LangGraph reducers. For the `messages` field (which uses `add_messages` reducer), updates accumulate rather than replace. + +**Command Accumulation**: When middleware returns `ExtendedModelResponse` with `Command`, multiple commands are accumulated in a list (inner-first, then outer). The agent applies them sequentially after the model node completes. + +**Reducer Semantics**: Non-reducer fields in later commands override earlier ones (outermost middleware wins). The `messages` field is special: reducer-based fields like `messages` accumulate through `add_messages`. + +## Writing Custom Middleware + +### Simple Example: Logging Middleware + +```python +from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse + +class LoggingMiddleware(AgentMiddleware): + def before_model(self, state, runtime): + num_messages = len(state.get("messages", [])) + print(f"[before_model] {num_messages} messages in state") + return None # No state updates + + def after_model(self, state, runtime): + last_msg = state["messages"][-1] if state["messages"] else None + if hasattr(last_msg, 'tool_calls'): + print(f"[after_model] Model requested {len(last_msg.tool_calls)} tools") + return None +``` + +### Retry Logic with Exponential Backoff + +```python +import asyncio +from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse +from langchain_core.messages import AIMessage + +class CustomRetryMiddleware(AgentMiddleware): + def __init__(self, max_retries: int = 2, backoff_factor: float = 2.0): + super().__init__() + self.max_retries = max_retries + self.backoff_factor = backoff_factor + + def wrap_model_call(self, request, handler): + for attempt in range(self.max_retries + 1): + try: + return handler(request) + except (TimeoutError, ConnectionError) as e: + if attempt < self.max_retries: + delay = self.backoff_factor ** attempt + time.sleep(delay) + else: + # Return error message instead of raising + return ModelResponse( + result=[AIMessage(content=f"Model call failed after {self.max_retries} retries: {e}")] + ) +``` + +### Tool Argument Validation and Modification + +```python +from langchain.agents.middleware import AgentMiddleware, ToolCallRequest +from langchain_core.messages import ToolMessage + +class ArgumentValidationMiddleware(AgentMiddleware): + def wrap_tool_call(self, request, handler): + tool_call = request.tool_call + args = tool_call["args"] + + # Example: ensure all string args are lowercase for a search tool + if tool_call["name"] == "search": + args = {k: v.lower() if isinstance(v, str) else v for k, v in args.items()} + request = request.override(tool_call={**tool_call, "args": args}) + + return handler(request) +``` + +### Conditional Tool Interception Based on State + +```python +from langchain.agents.middleware import AgentMiddleware, ToolCallRequest + +class ConditionalToolMiddleware(AgentMiddleware): + def wrap_tool_call(self, request, handler): + # Inspect agent state + state = request.state + user_message = next( + (m.content for m in reversed(state["messages"]) if hasattr(m, "content")), + None + ) + + # Only allow certain tools if user explicitly requests them + if "search" not in user_message.lower() and request.tool_call["name"] == "search": + return ToolMessage( + content="Search tool requires explicit user approval.", + tool_call_id=request.tool_call["id"], + name=request.tool_call["name"], + status="error", + ) + + return handler(request) +``` + +## Async and Sync Implementations + +Middleware can provide sync-only, async-only, or both implementations. The agent will: + +- Call sync versions (`wrap_model_call`, `wrap_tool_call`) in sync contexts (`stream()`, `invoke()`). +- Call async versions (`awrap_model_call`, `awrap_tool_call`) in async contexts (`astream()`, `ainvoke()`). +- Raise `NotImplementedError` if the required implementation is missing for the execution path. + +**Best Practice**: Implement both versions unless the middleware is inherently async-only (e.g., uses async I/O). + +## Integration with Agent Factory + +Middleware is passed to `create_agent()` as a list: + +```python +from langchain.agents import create_agent +from langchain.agents.middleware import ModelRetryMiddleware, ToolErrorMiddleware, HumanInTheLoopMiddleware + +agent = create_agent( + model=ChatOpenAI(), + tools=[search, calculator], + middleware=[ + ModelRetryMiddleware(max_retries=2), # Outermost: retry model calls + ToolErrorMiddleware(on_error=my_error_handler), # Middle: handle tool errors + HumanInTheLoopMiddleware(interrupt_on={"delete": True}), # Innermost: approve risky tools + ], +) +``` + +**Composition Rule**: First in the list = outermost (highest priority for interception and response transformation). + +## Tracing and Observability + +### Trace Policy + +Middleware can configure what gets traced via `trace_policy`: + +```python +from langchain.agents.middleware import TracePolicy, omit_payload + +class MyMiddleware(AgentMiddleware): + trace_policy = TracePolicy(process_inputs=omit_payload) + # Omit input payloads from traces to reduce noise, keep spans and timing +``` + +### Hook Naming in Traces + +Middleware hooks are automatically named in trace spans as `{middleware_name}.{hook_name}`, e.g., `ModelRetryMiddleware.wrap_model_call`, making it easy to identify which middleware handled each span. + +## Common Patterns and Anti-Patterns + +### ✅ Good Patterns + +1. **Immutable Requests**: Use `request.override()` instead of mutating fields. +2. **Opt-in Error Handling**: Return `None` from error handlers to propagate exceptions (don't silently swallow). +3. **State-Driven Decisions**: Use `request.state` and `runtime` to make decisions; avoid global state. +4. **Clear Composition Order**: Document which middleware should run first, especially when order matters (e.g., retry before error handling). +5. **Fallback Behavior**: Implement sync and async versions to support both execution paths. + +### ❌ Anti-Patterns + +1. **Mutating Request/Response Objects**: Direct assignment to `ModelRequest` fields is deprecated; always use `override()`. +2. **Silent Failures**: Don't catch and suppress exceptions in `on_error` handlers without returning content; let exceptions propagate if not handled. +3. **Hard-Coded Assumptions**: Don't assume specific tool names or message formats without validation. +4. **Blocking Async**: Never use `asyncio.run()` or sync I/O in async middleware implementations. +5. **Over-Composition**: Don't add more middleware than necessary; each layer adds latency. + +## Key Invariants and Lifecycle + +- **Immutability**: `ModelRequest` and `ToolCallRequest` follow an immutable pattern; use override/replace methods. +- **Execution Order**: Middleware runs in registration order (first = outermost). Multiple calls within a middleware (retries) do not reorder subsequent middleware. +- **State Reducer Semantics**: State updates via dict returns merge using reducer semantics. Messages accumulate; non-reducer fields are overwritten by the most recent update. +- **Command Flow**: `ExtendedModelResponse` commands are collected (inner-to-outer) and applied after the model node completes. +- **Sync/Async Consistency**: Choosing execution path (sync or async) is determined at agent invocation time; middleware cannot switch contexts mid-execution. +- **Exception Propagation**: Exceptions that are not explicitly handled propagate to the caller, unless a middleware converts them to a message or command. + +## See Also + +- [Agent Execution Flow and Loop Control](/openwiki/agent-execution.md) – Detailed description of the agent loop, state management, and where middleware hooks are invoked. +- [Agent Factory and Graph Construction](/openwiki/agent-factory.md) – How the agent graph is built, including middleware integration and handler composition. diff --git a/openwiki/model-initialization.md b/openwiki/model-initialization.md new file mode 100644 index 0000000000..94bb74dc34 --- /dev/null +++ b/openwiki/model-initialization.md @@ -0,0 +1,631 @@ +--- +type: Factory +title: Chat Model Initialization with init_chat_model +description: Factory function for instantiating chat models from provider strings with unified configuration and runtime model switching. +tags: [chat-models, factory-pattern, initialization, model-parameters, configuration, provider-registry] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-c479d4fffee5cf62576699e4 + resource: repo://libs/langchain_v1/langchain/chat_models/base.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +`init_chat_model` is a factory function that creates chat model instances from a unified interface. It centralizes model instantiation across all supported provider integrations (OpenAI, Anthropic, Bedrock, Google Vertex AI, etc.), handles parameter routing to provider-specific constructors, and supports runtime model configuration via LangChain's `Runnable` configuration system. + +The factory accepts a **model name with optional provider prefix** (e.g., `"openai:gpt-4"`, `"anthropic:claude-opus-4-7"`), infers the provider when unspecified, retrieves the provider's integration package, and instantiates the corresponding chat model class with translated kwargs. + +**Core responsibilities:** +- Accept and parse model identifiers with or without provider prefixes +- Infer providers from model name prefixes using heuristics +- Dynamically import provider integration packages and classes +- Route provider-specific kwargs to provider constructors +- Support both fixed (non-configurable) and runtime-configurable (switch model/provider at invoke time) initialization modes +- Route requests through LangSmith gateway when provider is `langsmith` + +## Location + +**File**: `repo://libs/langchain_v1/langchain/chat_models/base.py` + +**Public export**: `repo://libs/langchain_v1/langchain/chat_models/__init__.py#L5` + +## Function Signature + +```python +def init_chat_model( + model: str | None = None, + *, + model_provider: str | None = None, + configurable_fields: Literal["any"] | list[str] | tuple[str, ...] | None = None, + config_prefix: str | None = None, + **kwargs: Any, +) -> BaseChatModel | _ConfigurableModel +``` + +**Parameters:** + +- `model` (`str | None`): Model identifier, optionally with provider prefix (`"provider:model-name"`). If `None`, returns a configurable model that requires model name at runtime. Examples: + - `"openai:gpt-5.5"` (explicit prefix) + - `"gpt-5.5"` (inferred as OpenAI) + - `None` (configurable at runtime) + +- `model_provider` (`str | None`): Provider name as an alternative to prefix format. Used when provider is dynamic or needs to be independently configurable. Normalized to lowercase with underscores (e.g., `"azure-openai"` → `"azure_openai"`). + +- `configurable_fields` (`Literal["any"] | list[str] | tuple[str, ...] | None`): + - `None`: No fields are configurable (fixed model, default if `model` is specified) + - `"any"`: All parameters become configurable at runtime (⚠️ security: includes api_key, base_url) + - `list[str] | tuple[str, ...]`: Specified parameter names (e.g., `("temperature", "max_tokens")`) are configurable + - Defaults to `("model", "model_provider")` if `model` is `None` + +- `config_prefix` (`str | None`): Optional namespace prefix for runtime config keys. Used when multiple configurable models exist in the same application. Config is accessed via `config["configurable"]["{config_prefix}_{param}"]`. + +- `**kwargs`: Provider-specific parameters passed to the underlying chat model's constructor. Common parameters: + - `temperature` (`float`): Randomness control (0–1 or provider-specific range) + - `max_tokens` (`int`): Maximum output tokens + - `timeout` (`float`): Request timeout in seconds + - `max_retries` (`int`): Retry attempt limit + - `base_url` (`str`): Custom API endpoint (OpenAI-compatible) + - `rate_limiter` (`BaseRateLimiter`): Rate limiting instance + - Provider-specific: `openai_api_key`, `anthropic_api_url`, `bedrock_region`, etc. + +**Returns:** + +- `BaseChatModel`: A fixed (non-configurable) chat model when `configurable_fields` is `None` (the default if `model` is specified) +- `_ConfigurableModel`: A wrapper runnable that defers model instantiation until `invoke`/`stream` is called with configuration, enabling runtime model selection + +**Raises:** + +- `TypeError`: If `model` is not a string (e.g., a model object is passed) +- `ValueError`: If provider cannot be inferred or is not supported +- `ImportError`: If the provider's integration package is not installed + +## Model Name Parsing and Provider Inference + +### Explicit Provider Prefix + +If a colon (`:`) divides the model string and the prefix is a registered provider, it is extracted: + +```python +init_chat_model("openai:gpt-5.5") # provider='openai', model='gpt-5.5' +init_chat_model("anthropic:claude-opus-4-7") # provider='anthropic', model='claude-opus-4-7' +``` + +### Bare Model Name with Inference + +Without an explicit prefix, `_attempt_infer_model_provider` uses case-insensitive prefix matching: + +| Model Prefix | Inferred Provider | +|---|---| +| `gpt-`, `o1`, `o3`, `chatgpt`, `text-davinci` | `openai` | +| `claude` | `anthropic` | +| `command` | `cohere` | +| `accounts/fireworks` | `fireworks` | +| `gemini` | `google_vertexai` (⚠️ deprecated default; changing to `google_genai` in next major release) | +| `amazon.`, `anthropic.`, `meta.` | `bedrock` | +| `mistral`, `mixtral` | `mistralai` | +| `deepseek` | `deepseek` | +| `grok` | `xai` | +| `sonar` | `perplexity` | +| `solar` | `upstage` | + +**Example:** + +```python +init_chat_model("gpt-4") # inferred as openai:gpt-4 +init_chat_model("claude-sonnet-4-5-20250929") # inferred as anthropic +``` + +If inference fails and `model_provider` is not provided, a `ValueError` lists supported providers and suggests the documentation. + +## Built-in Provider Registry + +The `_BUILTIN_PROVIDERS` dictionary maps provider names to module paths, class names, and instantiation functions. Each entry is a tuple: `(module_path, class_name, creator_func)`. + +**Representative Entries** (repo://libs/langchain_v1/langchain/chat_models/base.py#L56-L97): + +| Provider | Package | Class | Module | Notes | +|---|---|---|---|---| +| `openai` | `langchain-openai` | `ChatOpenAI` | `langchain_openai` | | +| `anthropic` | `langchain-anthropic` | `ChatAnthropic` | `langchain_anthropic` | | +| `azure_openai` | `langchain-openai` | `AzureChatOpenAI` | `langchain_openai` | | +| `azure_ai` | `langchain-azure-ai` | `AzureAIOpenAIApiChatModel` | `langchain_azure_ai.chat_models` | Submodule import | +| `google_vertexai` | `langchain-google-vertexai` | `ChatVertexAI` | `langchain_google_vertexai` | | +| `google_genai` | `langchain-google-genai` | `ChatGoogleGenerativeAI` | `langchain_google_genai` | | +| `anthropic_bedrock` | `langchain-aws` | `ChatAnthropicBedrock` | `langchain_aws` | Bedrock-hosted Anthropic | +| `bedrock` | `langchain-aws` | `ChatBedrock` | `langchain_aws` | Generic Bedrock models | +| `bedrock_converse` | `langchain-aws` | `ChatBedrockConverse` | `langchain_aws` | Bedrock Converse API | +| `cohere` | `langchain-cohere` | `ChatCohere` | `langchain_cohere` | | +| `deepseek` | `langchain-deepseek` | `ChatDeepSeek` | `langchain_deepseek` | | +| `fireworks` | `langchain-fireworks` | `ChatFireworks` | `langchain_fireworks` | | +| `groq` | `langchain-groq` | `ChatGroq` | `langchain_groq` | | +| `huggingface` | `langchain-huggingface` | `ChatHuggingFace` | `langchain_huggingface` | Uses `from_model_id()` | +| `ibm` | `langchain-ibm` | `ChatWatsonx` | `langchain_ibm` | Uses `model_id=` param | +| `litellm` | `langchain-litellm` | `ChatLiteLLM` | `langchain_litellm` | | +| `mistralai` | `langchain-mistralai` | `ChatMistralAI` | `langchain_mistralai` | | +| `nvidia` | `langchain-nvidia-ai-endpoints` | `ChatNVIDIA` | `langchain_nvidia_ai_endpoints` | | +| `ollama` | `langchain-ollama` | `ChatOllama` | `langchain_ollama` | Fallback to `langchain_community` | +| `openrouter` | `langchain-openrouter` | `ChatOpenRouter` | `langchain_openrouter` | | +| `perplexity` | `langchain-perplexity` | `ChatPerplexity` | `langchain_perplexity` | | +| `together` | `langchain-together` | `ChatTogether` | `langchain_together` | | +| `upstage` | `langchain-upstage` | `ChatUpstage` | `langchain_upstage` | | +| `xai` | `langchain-xai` | `ChatXAI` | `langchain_xai` | | +| `langsmith` | `langchain-openai` | `ChatOpenAI` | `langchain_openai` | Routes via LangSmith gateway | + +**Design notes:** + +- The registry is **not exhaustive**. Unlisted providers can still be used if their integration package is installed, but model name inference will not work; `model_provider` must be specified. +- Most entries use the standard `_call` creator function, which directly instantiates the class. +- Special creators: `huggingface` uses `from_model_id(model_id=...)`, `ibm` uses `model_id=...`, `langsmith` wraps instantiation with gateway configuration. + +## Parameter Mapping and Creator Functions + +The `_get_chat_model_creator` function retrieves the provider's creator function and returns a partially-applied callable: + +```python +@functools.lru_cache(maxsize=len(_BUILTIN_PROVIDERS)) +def _get_chat_model_creator(provider: str) -> Callable[..., BaseChatModel]: + # Look up provider in registry + pkg, class_name, creator_func = _BUILTIN_PROVIDERS[provider] + # Import module and get class + module = _import_module(pkg, class_name) + cls = getattr(module, class_name) + # Return partial with class bound + return functools.partial(creator_func, cls=cls) +``` + +**Standard Creator (`_call`)**: + +```python +def _call(cls: type[BaseChatModel], **kwargs: Any) -> BaseChatModel: + return cls(**kwargs) +``` + +Forwards all kwargs directly to the provider's `__init__`. + +**Special Creators**: + +- **HuggingFace**: `lambda cls, model, **kwargs: cls.from_model_id(model_id=model, **kwargs)` + - Uses the class method `from_model_id` instead of direct instantiation + - `model` parameter becomes `model_id=` + +- **IBM**: `lambda cls, model, **kwargs: cls(model_id=model, **kwargs)` + - Maps `model` to `model_id=` parameter in constructor + +- **LangSmith Gateway** (`_init_langsmith`): + - Calls `_apply_gateway_config` to inject gateway credentials and base URL from environment (or `LANGSMITH_GATEWAY` URL) + - Sets `use_responses_api=True` to enable compatibility with gateway + - Falls back to `LANGSMITH_API_KEY` if `LANGSMITH_GATEWAY_API_KEY` not set + +```python +def _init_langsmith(cls: type[BaseChatModel], **kwargs: Any) -> BaseChatModel: + _apply_gateway_config( + kwargs, + cls, + base_url_field="openai_api_base", + api_key_field="openai_api_key", + provider_path="v1", + api_key_env=("LANGSMITH_GATEWAY_API_KEY", "LANGSMITH_API_KEY"), + default_base_url="https://gateway.smith.langchain.com/v1", + ) + kwargs["use_responses_api"] = True + return cls(**kwargs) +``` + +**Parameter Routing**: + +All `**kwargs` passed to `init_chat_model` are forwarded to the provider's constructor. The provider's parameter validation enforces which kwargs are accepted. Common cross-provider kwargs (`temperature`, `max_tokens`, `timeout`, `max_retries`) work on most providers; provider-specific kwargs (e.g., `openai_api_key`, `anthropic_api_url`) are only valid for their target provider. + +## Fixed Model Initialization + +When `model` is specified and `configurable_fields` is `None` (default), `init_chat_model` immediately instantiates and returns a `BaseChatModel`: + +```python +init_chat_model("gpt-4", temperature=0.7, max_tokens=500) +# Returns ChatOpenAI instance, ready to invoke +``` + +**Control flow** (repo://libs/langchain_v1/langchain/chat_models/base.py#L515-L520): + +```python +if not configurable_fields: + return _init_chat_model_helper( + cast("str", model), + model_provider=model_provider, + **kwargs, + ) +``` + +The `_init_chat_model_helper` function parses the model, retrieves the creator, and instantiates: + +```python +def _init_chat_model_helper( + model: str, + *, + model_provider: str | None = None, + **kwargs: Any, +) -> BaseChatModel: + model, model_provider = _parse_model(model, model_provider) + creator_func = _get_chat_model_creator(model_provider) + return creator_func(model=model, **kwargs) +``` + +**Error handling:** + +- If the package is missing: `ImportError` with suggestion to `pip install ` +- If the provider is unknown: `ValueError` listing all supported providers +- If model_provider inference fails: `ValueError` with docs link + +## Runtime-Configurable Model Initialization + +When `configurable_fields` is not `None` or `model` is `None`, `init_chat_model` returns a `_ConfigurableModel`, a `Runnable` wrapper that defers instantiation until config is provided at invoke time. + +**Use cases:** + +1. **No default model** – select model at runtime: + ```python + model = init_chat_model() # No model specified + model.invoke("hello", config={"configurable": {"model": "gpt-4"}}) + model.invoke("hello", config={"configurable": {"model": "claude-opus-4-7"}}) + ``` + +2. **Default model, switchable parameters** – override specific fields at runtime: + ```python + model = init_chat_model( + "gpt-4", + configurable_fields=("temperature", "max_tokens"), + temperature=0.5, + max_tokens=100 + ) + model.invoke( + "hello", + config={"configurable": {"temperature": 0.9, "max_tokens": 500}} + ) + ``` + +3. **Default model, fully configurable** – switch model or any parameter at runtime: + ```python + model = init_chat_model( + "gpt-4", + configurable_fields="any", # All fields configurable + config_prefix="my_model" + ) + model.invoke("hello") # Uses gpt-4, temperature=None + model.invoke( + "hello", + config={ + "configurable": { + "my_model_model": "claude-opus-4-7", + "my_model_temperature": 0.8 + } + } + ) + ``` + +### _ConfigurableModel + +**Location**: `repo://libs/langchain_v1/langchain/chat_models/base.py#L657-L1050` + +`_ConfigurableModel` is a `Runnable[LanguageModelInput, Any]` that queues model initialization and operations until a config is provided: + +**State:** +- `_default_config`: Dictionary of default parameter values (e.g., `{"model": "gpt-4", "temperature": 0.5}`) +- `_configurable_fields`: Which fields can be overridden at runtime (`"any"`, a list of field names) +- `_config_prefix`: Namespace for config keys (e.g., `"my_model_"`) +- `_queued_declarative_operations`: List of method calls (e.g., `bind_tools`, `with_structured_output`) to apply after model instantiation + +**Lifecycle:** + +1. **Instantiation**: `init_chat_model(...)` creates `_ConfigurableModel` with default config and queued operations +2. **Declarative operations** (e.g., `.bind_tools(...)`): Operations are queued; a new `_ConfigurableModel` is returned without mutation +3. **Invocation** (e.g., `.invoke(..., config=...)`): `_model(config)` is called to: + - Merge default and runtime config + - Call `_init_chat_model_helper` to instantiate the actual model + - Apply all queued operations in order + - Return the configured model instance +4. **Streaming/batch operations** delegate to the instantiated model + +**Config merging** (repo://libs/langchain_v1/langchain/chat_models/base.py#L711-L727): + +```python +def _model(self, config: RunnableConfig | None = None) -> Runnable[Any, Any]: + params = {**self._default_config, **self._model_params(config)} + model = _init_chat_model_helper(**params) + for name, args, kwargs in self._queued_declarative_operations: + model = getattr(model, name)(*args, **kwargs) + return model + +def _model_params(self, config: RunnableConfig | None) -> dict[str, Any]: + config = ensure_config(config) + # Extract configurable params and remove prefix + model_params = { + _remove_prefix(k, self._config_prefix): v + for k, v in config.get("configurable", {}).items() + if k.startswith(self._config_prefix) + } + # Filter to only allowed fields if not "any" + if self._configurable_fields != "any": + model_params = {k: v for k, v in model_params.items() if k in self._configurable_fields} + return model_params +``` + +**Declarative operations** (repo://libs/langchain_v1/langchain/chat_models/base.py#L681-L702): + +Methods like `bind_tools` and `with_structured_output` are intercepted and queued instead of applied immediately: + +```python +def __getattr__(self, name: str) -> Any: + if name in _DECLARATIVE_METHODS: + def queue(*args: Any, **kwargs: Any) -> _ConfigurableModel: + queued_declarative_operations = list(self._queued_declarative_operations) + queued_declarative_operations.append((name, args, kwargs)) + return _ConfigurableModel( + default_config=dict(self._default_config), + configurable_fields=self._configurable_fields, + config_prefix=self._config_prefix, + queued_declarative_operations=queued_declarative_operations, + ) + return queue + # ... delegate to default model if one exists +``` + +**Caching**: Creator functions are cached with `@functools.lru_cache` to avoid redundant module imports. + +## Common Parameter Mapping Examples + +### Temperature and max_tokens + +These are nearly universal but have different default values and ranges per provider: + +```python +# OpenAI: temperature 0–2 (default 1) +init_chat_model("gpt-4", temperature=0.7, max_tokens=500) + +# Anthropic: temperature 0–1 (default 1) +init_chat_model("claude-opus-4-7", temperature=0.7, max_tokens=500) + +# Google Vertex AI: temperature 0–2 +init_chat_model("google_vertexai:gemini-1.5-pro", temperature=0.7) +``` + +Check the provider's integration documentation for exact ranges and defaults. + +### API Keys and Base URLs + +Providers vary in parameter names: + +```python +# OpenAI: openai_api_key, openai_api_base +init_chat_model("gpt-4", openai_api_key="...", openai_api_base="https://custom.com/v1") + +# Anthropic: anthropic_api_key, anthropic_api_url +init_chat_model("claude-opus-4-7", anthropic_api_key="...", anthropic_api_url="https://custom.com") + +# Vertex AI: uses GCP credentials from environment, or project_id, location +init_chat_model("google_vertexai:gemini-1.5-pro", project_id="my-project") +``` + +Environment variable fallbacks are provider-specific; check the integration package docs. + +### Retry and Timeout + +Common cross-provider params: + +```python +init_chat_model( + "gpt-4", + max_retries=3, + timeout=30.0, +) +``` + +### Bedrock Region and Model IDs + +AWS Bedrock requires region and uses full model IDs: + +```python +init_chat_model( + "bedrock:amazon.titan-text-express-v1", + region_name="us-east-1", +) +``` + +## Testing and Example Patterns + +### Fixed Model Initialization + +```python +from langchain.chat_models import init_chat_model + +# Explicit provider prefix +llm = init_chat_model("openai:gpt-4", temperature=0) +response = llm.invoke("What is 2+2?") + +# Inferred provider +llm = init_chat_model("gpt-4", temperature=0) +response = llm.invoke("What is 2+2?") + +# Separate model_provider parameter +llm = init_chat_model("gpt-4", model_provider="openai", temperature=0) +``` + +### Configurable Model with Partial Override + +```python +from langchain.chat_models import init_chat_model + +model = init_chat_model( + "gpt-4", + configurable_fields=("temperature", "max_tokens"), + temperature=0.5, + max_tokens=100, +) + +# Use defaults +result = model.invoke("hello") + +# Override at runtime +result = model.invoke( + "hello", + config={ + "configurable": { + "temperature": 0.9, + "max_tokens": 500, + } + } +) +``` + +### Configurable Model with No Default + +```python +from langchain.chat_models import init_chat_model + +model = init_chat_model(temperature=0.5) # No model specified + +# Select model at runtime +result = model.invoke( + "hello", + config={"configurable": {"model": "gpt-4"}} +) + +result = model.invoke( + "hello", + config={"configurable": {"model": "claude-opus-4-7"}} +) +``` + +### Chaining with Prompts + +```python +from langchain.chat_models import init_chat_model +from langchain_core.prompts import ChatPromptTemplate + +model = init_chat_model("gpt-4", temperature=0) +prompt = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful assistant."), + ("user", "{input}"), +]) + +chain = prompt | model +response = chain.invoke({"input": "What is 2+2?"}) +``` + +### Binding Tools + +```python +from langchain.chat_models import init_chat_model +from pydantic import BaseModel, Field + +class Calculator(BaseModel): + """Perform arithmetic.""" + a: int = Field(..., description="First number") + b: int = Field(..., description="Second number") + +model = init_chat_model("gpt-4") +model_with_tools = model.bind_tools([Calculator]) + +result = model_with_tools.invoke("What is 2+2?") +``` + +### Configurable Model with Tools + +```python +from langchain.chat_models import init_chat_model +from pydantic import BaseModel, Field + +class Calculator(BaseModel): + """Perform arithmetic.""" + a: int = Field(..., description="First number") + b: int = Field(..., description="Second number") + +model = init_chat_model( + "gpt-4", + configurable_fields=("model", "model_provider"), +) +model_with_tools = model.bind_tools([Calculator]) + +# Use with default gpt-4 +result = model_with_tools.invoke("What is 2+2?") + +# Switch to Claude at runtime +result = model_with_tools.invoke( + "What is 2+2?", + config={"configurable": {"model": "claude-opus-4-7"}} +) +``` + +## LangSmith Gateway Integration + +The `langsmith` provider bridges to LangSmith's unified LLM gateway, allowing multiple non-OpenAI models to be served through a common OpenAI-compatible API: + +```python +init_chat_model("langsmith:moonshotai/kimi-k3") +``` + +**Configuration flow**: + +1. `_init_langsmith` is invoked instead of standard `_call` +2. `_apply_gateway_config` reads: + - `LANGSMITH_GATEWAY` or defaults to `https://gateway.smith.langchain.com` + - `LANGSMITH_GATEWAY_API_KEY` (preferred) or `LANGSMITH_API_KEY` + - Injects these as `openai_api_base` and `openai_api_key` +3. Sets `use_responses_api=True` for compatibility +4. Returns a `ChatOpenAI` instance pointing to the gateway + +**Example**: + +```python +import os +os.environ["LANGSMITH_GATEWAY_API_KEY"] = "..." +model = init_chat_model("langsmith:moonshotai/kimi-k3") +# Routes to https://gateway.smith.langchain.com/v1/chat/completions +``` + +## Extension and Adding New Providers + +To add support for a new provider integration, update `_BUILTIN_PROVIDERS` in `repo://libs/langchain_v1/langchain/chat_models/base.py`: + +1. Add an entry: `"provider_name": (module_path, ClassName, creator_func)` +2. If using standard instantiation, use `_call` +3. If the constructor uses a non-standard parameter for model name, create a custom creator +4. Ensure the provider module exports the class at the specified module path +5. The integration package must be pip-installable and named `langchain-` (with underscores converted to hyphens) + +Example for a hypothetical "myai" provider: + +```python +_BUILTIN_PROVIDERS = { + ... + "myai": ("langchain_myai", "ChatMyAI", _call), +} +``` + +Then install with `pip install langchain-myai` and use: + +```python +init_chat_model("myai:my-model-v1") +# or +init_chat_model("my-model-v1", model_provider="myai") +``` + +Update model name prefix inference in `_attempt_infer_model_provider` if a stable, unambiguous prefix exists (e.g., all MyAI models start with `myai-`). + +## Security Considerations + +**API Key Exposure**: When `configurable_fields="any"`, all parameters including `api_key`, `openai_api_key`, `anthropic_api_key`, and `base_url` become runtime-configurable. In production, restrict configurable fields to safe parameters: + +```python +# ❌ Unsafe: accepts any field, including secrets +model = init_chat_model("gpt-4", configurable_fields="any") + +# ✅ Safe: whitelist only model switching and temperature +model = init_chat_model( + "gpt-4", + configurable_fields=("temperature", "max_tokens"), +) +``` + +**Runtime Configuration Source**: Validate that config dicts come from trusted sources. If config is derived from user input, filter keys to prevent unexpected parameter injection. diff --git a/openwiki/openai-provider.md b/openwiki/openai-provider.md new file mode 100644 index 0000000000..1672bd16d5 --- /dev/null +++ b/openwiki/openai-provider.md @@ -0,0 +1,698 @@ +--- +type: "Reference" +title: "Use async methods (ainvoke, astream)" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-1e66a9da38565f8901e651f4 + resource: repo://libs/partners/openai/langchain_openai/__init__.py + - id: openwiki-source-738512768ef81ae009b097ac + resource: repo://libs/partners/openai/langchain_openai/chat_models/base.py + - id: openwiki-source-74e5bef080f1af7da12371cf + resource: repo://libs/partners/openai/langchain_openai/data/_profiles.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +The OpenAI integration (`langchain-openai`) provides production-ready chat model support for OpenAI's API and OpenAI-compatible endpoints. `ChatOpenAI` is the primary class that wraps OpenAI's Chat Completions and Responses APIs, with full support for: + +- **Chat Completions API** for standard generation and function calling +- **Responses API** for streaming, reasoning models, and enhanced features +- **Structured Output** via tool calling (`json_schema`), JSON mode, or function calling +- **Tool calling** with `bind_tools()` and `tool_choice` parameters +- **Vision** support for gpt-4-vision and gpt-4o models with image inputs +- **Streaming tokens** via callback integration with per-chunk timeouts +- **Model profiles** with capability metadata (input/output modalities, max tokens, tool support) + +**Core Principle**: `ChatOpenAI` targets [official OpenAI API specifications](https://github.com/openai/openai-openapi) only. Non-standard response fields added by third-party providers (e.g., `reasoning_content` on vLLM, `reasoning_details` on DeepSeek) are **not** extracted or preserved. For provider-specific features, use the corresponding provider-specific LangChain package (e.g., `ChatDeepSeek`, `ChatOpenRouter`). + +## Location + +**Package**: `repo://libs/partners/openai/langchain_openai/` + +**Main Class**: `repo://libs/partners/openai/langchain_openai/chat_models/base.py#L2799-L2900` + +**Exports**: `repo://libs/partners/openai/langchain_openai/__init__.py` + +Related classes: +- `BaseChatOpenAI`: Base implementation shared with Azure OpenAI +- `AzureChatOpenAI`: Azure-specific subclass in `repo://libs/partners/openai/langchain_openai/chat_models/azure.py` +- `OpenAI` (legacy): Completion-only model in `repo://libs/partners/openai/langchain_openai/llms/` + +## ChatOpenAI Class + +### Constructor Parameters + +**API Configuration:** + +- **`model`** (`str`, default `"gpt-3.5-turbo"`): OpenAI model identifier (e.g., `"gpt-4o"`, `"gpt-4-turbo"`, `"gpt-3.5-turbo"`). +- **`api_key`** (`str | Callable[[], str] | Callable[[], Awaitable[str]] | None`): API key for authentication. Can be: + - A string value + - A sync callable that returns a string + - An async callable that returns a string + - Inferred from `OPENAI_API_KEY` environment variable if not provided + + **Example:** Callable for dynamic key rotation + ```python + def get_api_key() -> str: + return fetch_from_secrets_manager() + + model = ChatOpenAI(api_key=get_api_key) + ``` + +- **`base_url`** (`str | None`): Custom API base URL for OpenAI-compatible endpoints. Resolution order (first match wins): + 1. Explicit `base_url` kwarg + 2. Environment variable `OPENAI_API_BASE` (read by LangChain at init) + 3. Environment variable `OPENAI_BASE_URL` (read by the underlying OpenAI SDK) + + When set, `stream_usage` is disabled by default since many non-OpenAI endpoints don't support streaming token usage. + +- **`organization`** (`str | None`): OpenAI organization ID. Inferred from `OPENAI_ORG_ID` environment variable. + +**Generation Parameters:** + +- **`temperature`** (`float | None`): Sampling temperature (0–2, typically 0–1). Controls randomness; higher = more random. +- **`max_tokens`** (`int | None`): Maximum tokens to generate in the response. +- **`top_p`** (`float | None`): Nucleus sampling probability. Cumulative probability threshold for token selection. +- **`top_logprobs`** (`int | None`): Number of most-likely tokens to return with log probabilities at each position (requires `logprobs=True`). +- **`logprobs`** (`bool | None`): Whether to return token log probabilities in the response. +- **`seed`** (`int | None`): Deterministic generation seed (if supported by the model). +- **`presence_penalty`** (`float | None`): Penalizes already-mentioned tokens (−2 to 2). +- **`frequency_penalty`** (`float | None`): Penalizes tokens by frequency in the response (−2 to 2). +- **`logit_bias`** (`dict[int, int] | None`): Modify likelihood of specific token IDs appearing. +- **`n`** (`int | None`): Number of completions to generate for each prompt. + +**Streaming & Latency:** + +- **`streaming`** (`bool`, default `False`): Enable streaming output via `stream()` and `astream()`. +- **`stream_usage`** (`bool | None`): Include token usage metadata in streaming chunks. + - `None` (default): Enabled for default OpenAI endpoint, disabled when `base_url` is set or custom client provided + - Set to `True`/`False` to override +- **`stream_chunk_timeout`** (`float | None`, default `120.0`): Per-chunk wall-clock timeout (seconds) for async streaming. Fires on silence between parsed chunks (not affected by OpenAI keepalive SSE comments). Set to `None` or `0` to disable. Overridable via `LANGCHAIN_OPENAI_STREAM_CHUNK_TIMEOUT_S` environment variable. + +**Request Handling:** + +- **`timeout`** (`float | tuple[float, float] | None`): Request timeout in seconds or `(connect_timeout, read_timeout)` tuple. +- **`max_retries`** (`int | None`): Maximum retry attempts for transient failures. +- **`http_client`** (`httpx.Client | None`): Custom sync HTTP client. Must be paired with `http_async_client` for async use. +- **`http_async_client`** (`httpx.AsyncClient | None`): Custom async HTTP client. +- **`http_socket_options`** (`Sequence[tuple[int, int, int]] | None`): TCP socket options `(level, option, value)` applied to httpx transports. Defaults to conservative TCP-keepalive + `TCP_USER_TIMEOUT` profile (~2-minute hang bound). Set to `()` (empty) to disable. Overridable via environment variables: `LANGCHAIN_OPENAI_TCP_KEEPALIVE`, `LANGCHAIN_OPENAI_TCP_KEEPIDLE`, `LANGCHAIN_OPENAI_TCP_KEEPINTVL`, `LANGCHAIN_OPENAI_TCP_KEEPCNT`, `LANGCHAIN_OPENAI_TCP_USER_TIMEOUT_MS`. + +**Advanced Features:** + +- **`reasoning_effort`** (`str | None`): For reasoning models, constrains reasoning effort. Values: `'minimal'`, `'low'`, `'medium'`, `'high'`. (Chat Completions API only.) +- **`reasoning`** (`dict[str, Any] | None`): Reasoning parameters for reasoning models (Responses API only). Shape: `{"effort": None | "low" | "medium" | "high", "summary": "auto" | "concise" | "detailed"}`. +- **`verbosity`** (`str | None`): Verbosity level for reasoning models (Responses API). Values: `'low'`, `'medium'`, `'high'`. +- **`service_tier`** (`str | None`): Latency tier for requests. Options: `'auto'`, `'default'`, `'flex'`. For users of OpenAI's scale tier service. +- **`store`** (`bool | None`): Whether OpenAI may store response data. Defaults to `True` for Responses API, `False` for Chat Completions API. +- **`include_response_headers`** (`bool`, default `False`): Capture response headers in message `response_metadata`. Useful for capturing provider metadata (e.g., served model names from inference providers). +- **`extra_body`** (`dict[str, Any] | None`): Additional JSON properties for OpenAI-compatible APIs (vLLM, LM Studio, etc.). Recommended over `model_kwargs` for provider-specific parameters. +- **`prompt_cache_options`** (`dict[str, Any] | None`): Configuration for OpenAI prompt caching. +- **`include`** (`list[str] | None`): Additional fields to include in generations from Responses API. Examples: `'file_search_call.results'`, `'message.input_image.image_url'`, `'reasoning.encrypted_content'`. +- **`truncation`** (`str | None`): Truncation strategy for Responses API. `'auto'` (drop middle items) or `'disabled'` (default). +- **`context_management`** (`list[dict[str, Any]] | None`): Configuration for [context compaction](https://developers.openai.com/api/docs/guides/compaction). +- **`disabled_params`** (`dict[str, Any] | None`): Parameters to disable for the model. Shape: `{"param": None | ['val1', 'val2']}`. Used to prevent incompatible parameters (e.g., `{"parallel_tool_calls": None}` for older models). + +**Other:** + +- **`stop`** (`list[str] | str | None`): Default stop sequences. +- **`tiktoken_model_name`** (`str | None`): Model name for tiktoken token counting (if different from `model`). +- **`model_kwargs`** (`dict[str, Any]`): Additional parameters passed to the API (overridden by `extra_body` for provider-specific params). +- **`default_headers`** (`dict[str, str] | None`): Custom HTTP headers for requests. +- **`default_query`** (`dict[str, object] | None`): Custom query parameters. + +### Initialization Examples + +**Basic Usage (API key from environment):** + +```python +from langchain_openai import ChatOpenAI + +model = ChatOpenAI(model="gpt-4o") +response = model.invoke("What is 2 + 2?") +``` + +**Custom API Base (OpenAI-compatible endpoint):** + +```python +model = ChatOpenAI( + model="gpt-4-turbo", + base_url="https://api.custom-openai-provider.com/v1", + api_key="your-custom-api-key" +) +``` + +**With Streaming and Timeout:** + +```python +model = ChatOpenAI( + model="gpt-4o", + streaming=True, + timeout=30.0, + stream_chunk_timeout=60.0 +) + +for chunk in model.stream("Hello, what is your name?"): + print(chunk.content, end="", flush=True) +``` + +**Dynamic API Key:** + +```python +async def get_api_key() -> str: + return await fetch_from_secret_store() + +model = ChatOpenAI( + model="gpt-4o", + api_key=get_api_key +) + +# Use async methods (ainvoke, astream) +response = await model.ainvoke("Hi") +``` + +## Model Profiles and Capabilities + +Model profiles are auto-generated metadata that describe model capabilities. They are stored in `repo://libs/partners/openai/langchain_openai/data/_profiles.py` and retrieved via the `ModelProfileRegistry`. + +**Profile Fields:** +- **`text_inputs` / `text_outputs`**: Text support. +- **`image_inputs`**: Vision support (gpt-4o, gpt-4-vision, gpt-4-turbo with vision). +- **`audio_inputs` / `audio_outputs`**: Audio support (gpt-4o, upcoming models). +- **`video_inputs`**: Video support (upcoming). +- **`tool_calling`**: Whether the model supports function/tool calling. +- **`structured_output`**: Whether the model supports JSON Schema structured output. +- **`max_input_tokens` / `max_output_tokens`**: Context window and generation limits. +- **`tool_call_streaming`**: Whether tool calls stream incrementally. +- **`tool_choice`**: Whether tool_choice parameter is supported. + +**Accessing Profiles:** + +```python +from langchain_openai import ChatOpenAI +from langchain_core.language_models import ModelProfileRegistry + +model = ChatOpenAI(model="gpt-4o") +# Profiles are used internally by LangChain for capability checks +``` + +## Vision Support + +Vision is supported on models like `gpt-4-vision`, `gpt-4o`, and `gpt-4-turbo`. Images can be provided as: + +1. **URL-based (`image_url`):** + ```python + from langchain_core.messages import HumanMessage + + message = HumanMessage( + content=[ + {"type": "text", "text": "What's in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "https://example.com/image.jpg", + "detail": "low" # or "high", "auto" + } + } + ] + ) + + model = ChatOpenAI(model="gpt-4o") + response = model.invoke(message) + ``` + +2. **Base64-encoded:** + ```python + import base64 + + with open("image.jpg", "rb") as f: + image_data = base64.b64encode(f.read()).decode("utf-8") + + message = HumanMessage( + content=[ + {"type": "text", "text": "Describe this image"}, + { + "type": "image_url", + "image_url": { + "url": f"data:image/jpeg;base64,{image_data}", + "detail": "auto" + } + } + ] + ) + ``` + +Token counting for images is approximated: `low` detail = 85 tokens, `high` detail = ~170 + 255 per image tile based on resolution. + +## Function Calling + +OpenAI's [function calling API](https://platform.openai.com/docs/guides/function-calling) (now called "tools" in the API) allows models to call functions you define. + +### `bind_tools()` Method + +Bind one or more tools to the model: + +```python +from langchain_core.tools import tool +from langchain_openai import ChatOpenAI + +@tool +def get_weather(location: str) -> str: + """Get weather for a location.""" + return f"Sunny in {location}" + +model = ChatOpenAI(model="gpt-4o") +bound_model = model.bind_tools([get_weather]) + +response = bound_model.invoke("What's the weather in Boston?") +print(response.tool_calls) +# [ToolCall(id='call_123', name='get_weather', args={'location': 'Boston'}, type='tool_call')] +``` + +**`bind_tools()` Signature:** + +```python +def bind_tools( + self, + tools: Sequence[dict | type | Callable | BaseTool], + *, + tool_choice: dict | str | bool | None = None, + strict: bool | None = None, + parallel_tool_calls: bool | None = None, + response_format: dict | type | None = None, + **kwargs: Any, +) -> Runnable[LanguageModelInput, AIMessage] +``` + +**Parameters:** + +- **`tools`**: List of tools. Supports: + - `BaseTool` instances (from `@tool` decorator) + - Pydantic `BaseModel` classes + - Callables with type hints + - Dicts (OpenAI tool schema) + +- **`tool_choice`** (`dict | str | bool | None`): Which tool(s) to force: + - `str` (tool name): Forces that specific tool (e.g., `"get_weather"`) + - `'auto'`: Auto-select tool or none (default) + - `'none'`: Prevent tool calling + - `'any'` / `'required'` / `True`: Force at least one tool call + - `dict`: OpenAI tool choice dict `{"type": "function", "function": {"name": "tool_name"}}` + - `False` / `None`: No effect, default behavior + + **Example:** + ```python + # Force specific tool + bound = model.bind_tools([get_weather, get_time], tool_choice="get_weather") + + # Force any tool + bound = model.bind_tools([get_weather, get_time], tool_choice=True) + + # Prevent tool use + bound = model.bind_tools([get_weather, get_time], tool_choice="none") + ``` + +- **`parallel_tool_calls`** (`bool | None`): Allow the model to call multiple tools in one response. Default: `None` (allow parallel). Set to `False` to disable. + + ```python + # Disable parallel tool calls (one at a time) + bound = model.bind_tools([get_weather, get_time], parallel_tool_calls=False) + ``` + +- **`strict`** (`bool | None`): If `True`, model output matches tool schema exactly. Schema is validated per OpenAI's [supported schemas](https://platform.openai.com/docs/guides/structured-outputs/supported-schemas). If `False`, no validation. If `None`, no strict requirement. + +- **`response_format`** (`dict | type | None`): Optional response schema for Chat Completions API. When set with tools, requires `strict=True` (exception: Responses API). + +### Tool Call Processing + +When a model calls tools, the response includes `AIMessage.tool_calls`: + +```python +response = bound_model.invoke("What's the weather in Boston and New York?") + +# response.tool_calls: +# [ +# ToolCall(id='call_1', name='get_weather', args={'location': 'Boston'}), +# ToolCall(id='call_2', name='get_weather', args={'location': 'New York'}) +# ] +``` + +**Process tool calls in an agentic loop:** + +```python +from langchain_core.messages import ToolMessage + +messages = [HumanMessage("What's the weather in Boston?")] + +while True: + response = model.invoke(messages) + + if not response.tool_calls: + print("Final response:", response.content) + break + + messages.append(response) + + for tool_call in response.tool_calls: + tool_result = get_weather(location=tool_call.args["location"]) + messages.append(ToolMessage(content=tool_result, tool_call_id=tool_call.id)) +``` + +## Structured Output + +The `with_structured_output()` method constrains model outputs to a specific schema. Three methods are available: + +### Method: `'function_calling'` (Default) + +Uses OpenAI's [tool-calling API](https://platform.openai.com/docs/guides/function-calling). The model must call a specific tool with arguments matching the schema. + +**Pros**: Supported on most models (gpt-3.5-turbo, gpt-4, etc.). + +**Cons**: Requires tool calling support. Less strict than `json_schema`. + +**Usage:** + +```python +from pydantic import BaseModel +from langchain_openai import ChatOpenAI + +class Joke(BaseModel): + setup: str + punchline: str + +model = ChatOpenAI(model="gpt-4o") +structured = model.with_structured_output(Joke, method="function_calling") + +result = structured.invoke("Tell me a joke") +print(result) +# Joke(setup='...', punchline='...') +``` + +### Method: `'json_schema'` + +Uses OpenAI's [Structured Output API](https://platform.openai.com/docs/guides/structured-outputs). The model generates JSON strictly matching the schema. + +**Pros**: Guaranteed strict output conformance. Supported on modern models (gpt-4o-2024-08-06+, gpt-4-turbo-2024-04-09+). + +**Cons**: Only for models with `structured_output=True` in profile. Requires valid JSON Schema. + +**Usage:** + +```python +structured = model.with_structured_output( + Joke, + method="json_schema", + strict=True # Validate schema and output +) + +result = structured.invoke("Tell me a joke") +print(result) # Pydantic instance if schema is BaseModel, else dict +``` + +### Method: `'json_mode'` + +Uses OpenAI's [JSON mode](https://platform.openai.com/docs/guides/structured-outputs/json-mode). The model generates JSON but without strict schema validation. + +**Pros**: Works on more models. Simpler than `json_schema`. + +**Cons**: Output may not strictly match schema. Manual prompt engineering required. + +**Usage:** + +```python +structured = model.with_structured_output( + Joke, + method="json_mode" +) + +# Must include instructions in your prompt +result = structured.invoke( + "Tell me a joke. Return as JSON: {setup: ..., punchline: ...}" +) +``` + +### Common Parameters + +```python +def with_structured_output( + self, + schema: dict | BaseModel | type | None = None, + *, + method: Literal["function_calling", "json_mode", "json_schema"] = "function_calling", + include_raw: bool = False, + strict: bool | None = None, + tools: list | None = None, + **kwargs: Any, +) -> Runnable[LanguageModelInput, dict | BaseModel] +``` + +- **`schema`**: Output schema. Accepts: + - Pydantic `BaseModel` (output is instance of this class) + - JSON Schema dict + - `TypedDict` + - OpenAI tool schema dict + +- **`method`**: Approach for constraining output. Defaults to `"function_calling"`. Override incompatible methods: + ```python + # For older models, auto-downgrade json_schema to function_calling + structured = model.with_structured_output( + Joke, + method="json_schema" # Auto-downgrades to function_calling if model doesn't support it + ) + ``` + +- **`include_raw`** (`bool`, default `False`): Return both raw model response and parsed output in a dict: + ```python + structured = model.with_structured_output( + Joke, + include_raw=True + ) + + result = structured.invoke("Tell me a joke") + # { + # 'raw': AIMessage(...), + # 'parsed': Joke(...), + # 'parsing_error': None + # } + ``` + + If parsing fails, `parsed` is `None` and `parsing_error` is the exception. + +- **`strict`** (`bool | None`): Validate schema and enforce exact output matching. Default: `None` (not enforced). Only applies to `json_schema` and `function_calling` methods. + +- **`tools`** (`list | None`): Additional tools the model can call (alongside structured output). Requires: + - `method="json_schema"` + - `strict=True` + - `include_raw=True` + + When the model calls a tool instead of generating structured output: + ```python + structured = model.with_structured_output( + ResponseSchema, + method="json_schema", + tools=[get_weather, search_web], + strict=True, + include_raw=True + ) + + result = structured.invoke("Should I bring an umbrella to Boston?") + # { + # 'raw': AIMessage(tool_calls=[ToolCall(name='get_weather', ...)]), + # 'parsed': None, + # 'parsing_error': None + # } + ``` + +## Streaming and Callbacks + +### Basic Streaming + +```python +model = ChatOpenAI(model="gpt-4o", streaming=True) + +for chunk in model.stream("Tell me a story"): + print(chunk.content, end="", flush=True) +``` + +### Token Callback Integration + +Streaming callbacks fire on each chunk via `run_manager.on_llm_new_token()`: + +```python +from langchain_core.callbacks import StreamingStdOutCallbackHandler + +model = ChatOpenAI(model="gpt-4o", streaming=True) + +# Callbacks are invoked during stream +for chunk in model.stream( + "Hello", + config={"callbacks": [StreamingStdOutCallbackHandler()]} +): + pass # Callback prints tokens as they arrive +``` + +**Custom Streaming Callback:** + +```python +from langchain_core.callbacks import BaseCallbackHandler + +class CustomTokenCallback(BaseCallbackHandler): + def on_llm_new_token(self, token: str, **kwargs) -> None: + print(f"[TOKEN] {token}") + +model = ChatOpenAI(model="gpt-4o", streaming=True) +model.invoke( + "Hi", + config={"callbacks": [CustomTokenCallback()]} +) +``` + +### Async Streaming with Chunk Timeout + +Async streaming operations apply `stream_chunk_timeout` (default 120s): + +```python +async def stream_response(): + model = ChatOpenAI( + model="gpt-4o", + streaming=True, + stream_chunk_timeout=30.0 # 30-second per-chunk timeout + ) + + async for chunk in model.astream("Tell me a long story"): + print(chunk.content, end="", flush=True) + +import asyncio +asyncio.run(stream_response()) +``` + +If a chunk doesn't arrive within the timeout, `StreamChunkTimeoutError` is raised. This is distinct from `httpx` read timeout—it measures silence between *parsed chunks*, not inter-byte silence. + +## Error Handling + +`ChatOpenAI` maps OpenAI SDK exceptions to LangChain's standardized error hierarchy: + +| OpenAI Exception | LangChain Class | Meaning | +|---|---|---| +| `AuthenticationError` | `ModelAuthenticationError` | Invalid API key | +| `PermissionDeniedError` | `ModelPermissionDeniedError` | API key lacks permissions | +| `BadRequestError` (context_length_exceeded) | `ContextOverflowError` | Input exceeds model's context window | +| `RateLimitError` | `ModelRateLimitError` | Rate limit exceeded | +| `NotFoundError` | `ModelNotFoundError` | Model doesn't exist or isn't available | +| `APIError` / `InternalServerError` | `ModelAPIError` | OpenAI server error | +| `APIConnectionError` | `ModelConnectionError` | Network connectivity issue | +| `APITimeoutError` | `ModelTimeoutError` | Request timeout | + +**Example:** + +```python +from langchain_core.exceptions import ContextOverflowError, ModelAuthenticationError + +try: + response = model.invoke(very_long_message) +except ContextOverflowError as e: + print(f"Message too long: {e}") +except ModelAuthenticationError as e: + print(f"Auth failed: {e}") +``` + +## Advanced Configuration + +### Proxy and Network + +```python +# Explicit proxy +model = ChatOpenAI( + model="gpt-4o", + openai_proxy="http://proxy.example.com:8080" +) + +# Or via environment: OPENAI_PROXY=... +``` + +### Custom HTTP Client + +```python +import httpx + +http_client = httpx.Client( + timeout=30.0, + limits=httpx.Limits(max_connections=10) +) + +model = ChatOpenAI( + model="gpt-4o", + http_client=http_client +) +``` + +### Prompt Caching + +```python +# Cache long system prompts or large context +model = ChatOpenAI( + model="gpt-4o", + prompt_cache_options={ + "type": "ephemeral" + } +) +``` + +### Logit Bias + +```python +# Encourage specific tokens +model = ChatOpenAI( + model="gpt-4o", + logit_bias={ + 20: 50, # Boost token ID 20 + 100: -100 # Suppress token ID 100 + } +) +``` + +## Model Name Examples + +**Current recommended models:** +- **`gpt-4o`**: Latest, multimodal, fastest (recommended for most use cases) +- **`gpt-4o-mini`**: Lightweight, cheaper variant +- **`gpt-4-turbo`**: Powerful, older than gpt-4o +- **`gpt-4`**: Original GPT-4 (deprecated) +- **`gpt-3.5-turbo`**: Legacy, still cheap (deprecated) + +Check [OpenAI models page](https://platform.openai.com/docs/models) for current list. + +## Testing + +Unit tests are located in `repo://libs/partners/openai/tests/unit_tests/chat_models/`. + +Key test files: +- `repo://libs/partners/openai/tests/unit_tests/chat_models/test_base.py`: Main ChatOpenAI tests +- `repo://libs/partners/openai/tests/unit_tests/chat_models/test_client_utils.py`: Client utilities +- `repo://libs/partners/openai/tests/unit_tests/chat_models/test_azure.py`: Azure-specific tests + +**Test structured output:** + +```python +from langchain_openai import ChatOpenAI +from pydantic import BaseModel + +class TestSchema(BaseModel): + name: str + value: int + +def test_with_structured_output(): + model = ChatOpenAI(model="gpt-4o") + structured = model.with_structured_output(TestSchema, method="function_calling") + # Invoke and verify output is TestSchema instance +``` + +## Related Pages + +- `/openwiki/model-initialization.md`: Factory function `init_chat_model()` for provider-agnostic model selection +- `/openwiki/chat-models.md`: Core `BaseChatModel` interface and lifecycle +- `/openwiki/messages.md`: Message types and content blocks (text, images, tool calls) diff --git a/openwiki/partner-pattern.md b/openwiki/partner-pattern.md new file mode 100644 index 0000000000..d2abea7a43 --- /dev/null +++ b/openwiki/partner-pattern.md @@ -0,0 +1,718 @@ +--- +type: Integration Pattern +title: Adding a New Chat Model Provider +description: Step-by-step guide to integrate a new LLM provider into LangChain's monorepo, including package structure, ChatModel implementation, streaming, function calling, and standard tests. +tags: [chat-models, provider-integration, llm, function-calling, structured-output, streaming] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-b32b84365d17276620c41ebc + resource: repo://libs/core/langchain_core/messages/base.py + - id: openwiki-source-c479d4fffee5cf62576699e4 + resource: repo://libs/langchain_v1/langchain/chat_models/base.py + - id: openwiki-source-e0b95eafb4bbd52f491c8cee + resource: repo://libs/model-profiles/README.md + - id: openwiki-source-7de1ace618efbdfd8bacb5cb + resource: repo://libs/partners/anthropic/langchain_anthropic/chat_models.py + - id: openwiki-source-f416dbc063b474398e38ff3c + resource: repo://libs/partners/anthropic/langchain_anthropic/data/_profiles.py + - id: openwiki-source-d14c2b8060843a8a89b74733 + resource: repo://libs/partners/anthropic/langchain_anthropic/data/profile_augmentations.toml + - id: openwiki-source-8641a971af4f11b852966d77 + resource: repo://libs/partners/openai/langchain_openai/chat_models/__init__.py + - id: openwiki-source-738512768ef81ae009b097ac + resource: repo://libs/partners/openai/langchain_openai/chat_models/base.py + - id: openwiki-source-df762860acfcc6abf0ce804b + resource: repo://libs/partners/openai/pyproject.toml + - id: openwiki-source-3953aa29dbaaf738e6efc09d + resource: repo://libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py + - id: openwiki-source-025cad4ae99967890152b7e0 + resource: repo://libs/standard-tests/README.md +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +This guide documents the integration pattern for adding a new chat model provider (LLM service) to the LangChain monorepo. A **provider** represents an LLM service (e.g., OpenAI, Anthropic, Mistral) with its own client library, model lineup, and API conventions. Each provider integration lives in its own package under `/libs/partners/` and provides a `ChatModel` subclass bridging LangChain's message abstraction to the provider's API. + +The integration process involves: + +1. **Package structure**: Creating `/libs/partners/provider_name/` with Python module, tests, and configuration +2. **ChatModel implementation**: Inheriting `BaseChatModel` and implementing generation/streaming methods +3. **Message conversion**: Translating between LangChain's unified message format and provider-specific API schemas +4. **Provider registration**: Adding the provider to the built-in `init_chat_model` factory registry +5. **Model profiles**: Publishing capability data (context window, tool calling, structured output, etc.) +6. **Standard tests**: Inheriting unit and integration test suites to validate the implementation + +## 1. Package Structure + +Create a new directory under `/libs/partners/` with the provider name in lowercase, using hyphens as needed: + +``` +/libs/partners/provider_name/ +├── langchain_provider_name/ # Python package +│ ├── __init__.py # Exports: ChatProviderModel, version +│ ├── _version.py # Version constant +│ ├── chat_models/ # Chat model implementation +│ │ ├── __init__.py +│ │ └── base.py # ChatProviderModel class +│ ├── data/ # Model profiles and augmentations +│ │ ├── __init__.py +│ │ ├── _profiles.py # Auto-generated profiles from models.dev +│ │ └── profile_augmentations.toml # Provider-specific overrides +│ ├── py.typed # PEP 561 marker for type checking +│ └── middleware/ # (Optional) Custom middleware +├── tests/ +│ ├── unit_tests/ +│ │ ├── __init__.py +│ │ └── chat_models/ +│ │ ├── test_standard.py # Standard unit test suite +│ │ └── test_*.py # Provider-specific unit tests +│ └── integration_tests/ +│ ├── __init__.py +│ └── chat_models/ +│ ├── test_standard.py # Standard integration test suite +│ └── test_*.py # Provider-specific integration tests +├── pyproject.toml # Package metadata and dependencies +├── Makefile # Common build/test targets +├── README.md # User-facing documentation +├── LICENSE # MIT license +└── uv.lock # Locked dependency versions + +``` + +### Package Metadata (pyproject.toml) + +Key configuration for a provider package (reference: `repo://libs/partners/openai/pyproject.toml`): + +```toml +[project] +name = "langchain-provider-name" # pypi package name +description = "LangChain integration for Provider Name" +requires-python = ">=3.10.0,<4.0.0" + +dependencies = [ + "langchain-core>=1.6.0,<2.0.0", # Required: base LangChain + "provider-client-library>=X.Y.Z", # Provider's own SDK + "certifi>=2024.6.2", # SSL certificates +] + +[dependency-groups] +test = [ + "pytest>=9.0.3", + "pytest-asyncio>=1.3.0", + "langchain>=1.0.0", + "langchain-tests>=1.1.9", # Standard test suite +] + +[tool.uv.sources] +langchain-core = { path = "../../core", editable = true } +langchain-tests = { path = "../../standard-tests", editable = true } +langchain = { path = "../../langchain_v1", editable = true } +``` + +## 2. ChatModel Implementation + +### BaseChatModel and Core Requirements + +All provider implementations must inherit from **`BaseChatModel`** (`repo://libs/core/langchain_core/language_models/chat_models.py#L284-L2400`), which defines the contract for invoking and streaming chat models. + +**Core responsibilities** (location: `repo://libs/partners/anthropic/langchain_anthropic/chat_models.py#L1-L150`): + +1. **Inherit `BaseChatModel`** with type parameter `[AIMessage]` +2. **Implement `_generate` method** (required sync): Transform messages into `ChatResult` with `ChatGeneration` objects wrapping `AIMessage` output +3. **Implement `_stream` method** (optional for streaming support): Yield `ChatGenerationChunk` objects containing `AIMessageChunk` with incremental tokens +4. **Implement `_agenerate` method** (async variant of `_generate`) or `_astream` method (async variant of `_stream`) +5. **Set `_llm_type` property**: Return the provider identifier string for identification + +### Minimal ChatModel Template + +```python +"""Provider chat model integration.""" + +from typing import Any, Iterator +from langchain_core.callbacks import CallbackManagerForLLMRun +from langchain_core.language_models import LanguageModelInput +from langchain_core.language_models.chat_models import BaseChatModel +from langchain_core.messages import AIMessage, BaseMessage +from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult + +class ChatProviderModel(BaseChatModel): + """Chat model for Provider Name.""" + + model: str # Model identifier (e.g., "model-123") + api_key: str | None = None # Provider API key + temperature: float = 1.0 # Temperature parameter + max_tokens: int | None = None # Max output tokens + + @property + def _llm_type(self) -> str: + """Return provider identifier.""" + return "provider_name" + + def _generate( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + """Generate chat completion synchronously. + + Args: + messages: Conversation history and user input + stop: Optional stop sequences + run_manager: Callback manager for logging + **kwargs: Additional provider-specific parameters + + Returns: + ChatResult containing one or more ChatGeneration objects + """ + # 1. Convert LangChain messages to provider format + provider_messages = self._convert_messages_to_provider_format(messages) + + # 2. Build request payload + payload = { + "model": self.model, + "messages": provider_messages, + "temperature": self.temperature, + "max_tokens": self.max_tokens, + "stop": stop, + **kwargs, + } + + # 3. Call provider API + response = self._client.chat.completions.create(**payload) + + # 4. Extract and convert response to AIMessage + content = response.choices[0].message.content + message = AIMessage( + content=content, + response_metadata={ + "model": response.model, + "stop_reason": response.choices[0].finish_reason, + }, + ) + + # 5. Return ChatResult with generation info + return ChatResult( + generations=[ChatGeneration(message=message)], + llm_output={"usage": response.usage.model_dump()} if response.usage else None, + ) + + def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + """Stream chat completion tokens in real time. + + This method is called when `stream=True` or streaming callbacks are attached. + + Args: + messages: Conversation history + stop: Optional stop sequences + run_manager: Callback manager for per-token callbacks + **kwargs: Additional parameters + + Yields: + ChatGenerationChunk objects containing AIMessageChunk with partial content + """ + # 1. Build streaming request + payload = { + "model": self.model, + "messages": self._convert_messages_to_provider_format(messages), + "stream": True, + "temperature": self.temperature, + "stop": stop, + **kwargs, + } + + # 2. Stream from API + accumulated_content = "" + for event in self._client.chat.completions.create(**payload): + # 3. Extract token delta + delta = event.choices[0].delta + if delta.content: + accumulated_content += delta.content + + # 4. Yield chunk with incremental token + chunk_message = AIMessageChunk(content=delta.content) + chunk = ChatGenerationChunk(message=chunk_message) + + # 5. Notify run_manager of new token + if run_manager: + run_manager.on_llm_new_token(delta.content, chunk=chunk) + + yield chunk + + def _convert_messages_to_provider_format( + self, messages: list[BaseMessage] + ) -> list[dict[str, Any]]: + """Translate LangChain messages to provider API format. + + Provider APIs often use a different schema for messages (e.g., different + role names, content representation). This method maps the unified LangChain + format to the provider's specific requirements. + """ + # Implementation: map LangChain message types to provider format + # Handle HumanMessage, AIMessage, SystemMessage, ToolMessage + pass +``` + +### Message Conversion and Content Blocks + +LangChain messages have a **unified, provider-agnostic content format** using **content blocks** (reference: `/openwiki/messages.md`). Each provider must translate between this format and its own API schema. + +**Key message fields:** + +- **`content`**: `str | list[dict]` - Either plain text or structured content blocks +- **`tool_calls`**: `list[ToolCall]` - Structured tool invocation requests from the model +- **`usage_metadata`**: Token counts and category breakdowns + +**Content block types:** +- `{"type": "text", "text": "..."}` - Plain text +- `{"type": "image", "source": {...}}` - Images (multiple source formats) +- `{"type": "tool_use", "id": "...", "name": "...", "input": {...}}` - Tool calls +- `{"type": "tool_result", ...}` - Tool execution results + +**Example: Message conversion (Anthropic reference):** + +The Anthropic provider converts LangChain messages to Anthropic's format (reference: `repo://libs/partners/anthropic/langchain_anthropic/chat_models.py#L1-L100`): + +```python +def _convert_messages_to_provider_format( + self, messages: list[BaseMessage] +) -> list[dict]: + """Convert LangChain messages to Anthropic API format.""" + provider_messages = [] + + for msg in messages: + if isinstance(msg, HumanMessage): + # Convert HumanMessage to Anthropic user role + provider_messages.append({ + "role": "user", + "content": self._format_content(msg.content), + }) + elif isinstance(msg, AIMessage): + # Convert AIMessage to Anthropic assistant role, including tool calls + content = self._format_content(msg.content) + if msg.tool_calls: + # Append structured tool_use blocks + content.extend([ + { + "type": "tool_use", + "id": tc["id"], + "name": tc["name"], + "input": tc["args"], + } + for tc in msg.tool_calls + ]) + provider_messages.append({ + "role": "assistant", + "content": content, + }) + elif isinstance(msg, SystemMessage): + provider_messages.append({ + "role": "user", + "content": msg.content, + }) + + return provider_messages +``` + +## 3. Streaming Architecture + +### Stream Implementation Pattern + +Streaming returns `AIMessageChunk` objects incrementally as the model generates tokens. The implementation must: + +1. **Enable streaming at request time** by setting the streaming flag on the provider API +2. **Iterate over provider events** (e.g., SSE chunks, iterator) +3. **Extract delta/token content** from each event +4. **Create `AIMessageChunk`** with incremental content +5. **Wrap in `ChatGenerationChunk`** for the generation abstraction +6. **Notify run_manager** via `on_llm_new_token` callback for observability + +**Key pattern** (Anthropic reference: `repo://libs/partners/anthropic/langchain_anthropic/chat_models.py#L1862-L1910`): + +```python +def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> Iterator[ChatGenerationChunk]: + # Enable streaming in API payload + kwargs["stream"] = True + payload = self._get_request_payload(messages, stop=stop, **kwargs) + + # Stream from API + raw_response = self._client.create(payload) + for event in raw_response.parse(): + # Convert Anthropic streaming event to AIMessageChunk + msg_chunk = self._make_message_chunk_from_anthropic_event(event) + + if msg_chunk is not None: + chunk = ChatGenerationChunk(message=msg_chunk) + + # Notify callbacks + if run_manager and isinstance(msg_chunk.content, str): + run_manager.on_llm_new_token(msg_chunk.content, chunk=chunk) + + yield chunk +``` + +### Async Streaming + +Implement `_astream` as the async variant of `_stream`, using `async for` instead of `for`: + +```python +async def _astream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + run_manager: AsyncCallbackManagerForLLMRun | None = None, + **kwargs: Any, +) -> AsyncIterator[ChatGenerationChunk]: + kwargs["stream"] = True + payload = self._get_request_payload(messages, stop=stop, **kwargs) + + raw_response = await self._acreate(payload) + async for event in await _aparse(raw_response): + msg_chunk = self._make_message_chunk_from_anthropic_event(event) + if msg_chunk is not None: + chunk = ChatGenerationChunk(message=msg_chunk) + if run_manager and isinstance(msg_chunk.content, str): + await run_manager.on_llm_new_token(msg_chunk.content, chunk=chunk) + yield chunk +``` + +## 4. Provider-Specific Features + +### Function Calling / Tool Use + +Implement `bind_tools()` (inherited from `BaseChatModel`) to support tool calling. Tools are converted to the provider's schema (OpenAI, Anthropic, JSON Schema, etc.) before sending to the API. The model response includes tool calls, which are extracted and populated in `AIMessage.tool_calls`. + +**Implementation approach:** + +1. **Accept `BaseTool` objects, Pydantic models, or dicts** via `bind_tools()` +2. **Convert to provider schema** using utility functions: + - `convert_to_openai_tool()` - For OpenAI-compatible APIs + - `convert_to_json_schema()` - For JSON Schema format + - Provider-specific converters for custom formats +3. **Include tools in API request** as part of the payload +4. **Parse tool calls** from the response into `ToolCall` objects +5. **Handle invalid/malformed tool calls** by storing them in `invalid_tool_calls` + +### Structured Output + +Implement `with_structured_output()` to enforce the model to return responses matching a Pydantic model or JSON schema. This typically maps to the provider's structured output or JSON mode feature. + +**Pattern:** + +1. Accept a Pydantic model or JSON schema +2. Convert to provider's structured output format +3. Include in API request +4. Parse response and validate against schema +5. Return parsed model instance or dict + +### Vision / Multimodal Input + +Support image, video, and audio inputs via content blocks: + +- **Images**: `{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": "..."}}` +- **Video/Audio**: Similar structure with appropriate media types + +Translate these to provider-specific formats (e.g., OpenAI's `image_url`, Anthropic's `source` block). + +## 5. Provider Registration in init_chat_model + +Add your provider to the **built-in registry** to enable automatic factory instantiation (`repo://libs/langchain_v1/langchain/chat_models/base.py#L56-L97`): + +```python +_BUILTIN_PROVIDERS: dict[str, tuple[str, str, Callable[..., BaseChatModel]]] = { + # ... existing providers ... + + "provider_name": ( + "langchain_provider_name", # Module path + "ChatProviderModel", # Class name + _call, # Instantiation function (_call is standard) + ), + + # Special case: custom instantiation function (e.g., IBM Watson) + # "ibm": ("langchain_ibm", "ChatWatsonx", lambda cls, model, **kwargs: cls(model_id=model, **kwargs)), +} +``` + +**After registration, users can instantiate your model:** + +```python +from langchain.chat_models import init_chat_model + +# With explicit provider prefix +model = init_chat_model("provider_name:model-id", temperature=0.5) + +# With inferred provider (if model name starts with unique prefix) +model = init_chat_model("unique-prefix-model-id") +``` + +**Provider inference heuristics** are defined in `_attempt_infer_model_provider()`: + +| Model Prefix | Inferred Provider | +|---|---| +| `gpt-`, `o1`, `o3` | `openai` | +| `claude` | `anthropic` | +| `mistral`, `mixtral` | `mistralai` | + +Add your provider's prefixes to the inference function to enable bare model name registration. + +## 6. Model Profiles + +**Model profiles** expose capability data (context window, supported modalities, tool calling, etc.) via `model.profile` property. Users and integrations query this to determine model capabilities. + +### Profile Structure and Data Source + +Profiles are dictionaries stored in `data/_profiles.py` and generated from the open-source [models.dev](https://github.com/sst/models.dev) project via the `langchain-model-profiles` CLI tool. + +**Sample profile** (reference: `repo://libs/partners/anthropic/langchain_anthropic/data/_profiles.py#L18-L52`): + +```python +_PROFILES: dict[str, dict[str, Any]] = { + "claude-opus-4-7": { + "name": "Claude Opus 4.7", + "release_date": "2025-09-01", + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "text_inputs": True, + "image_inputs": True, + "audio_inputs": False, + "pdf_inputs": True, + "tool_calling": True, + "structured_output": True, + "tool_call_streaming": True, + "reasoning_output": True, + }, + # ... more models ... +} +``` + +### Updating Profiles + +Use the `langchain-model-profiles` CLI tool to refresh profiles from models.dev: + +```bash +uv add langchain-model-profiles # Install once globally or in dev dependencies + +# Refresh profiles for your provider +langchain-model-profiles refresh \ + --provider provider_name \ + --data-dir ./langchain_provider_name/data +``` + +This downloads the latest model data, merges provider-specific augmentations from `profile_augmentations.toml`, and generates `_profiles.py`. + +### Provider Augmentations + +Create `data/profile_augmentations.toml` for LangChain-specific capability overrides (reference: `repo://libs/partners/anthropic/langchain_anthropic/data/profile_augmentations.toml`): + +```toml +provider = "provider_name" + +[overrides] +# Global overrides for all models +tool_call_streaming = true + +[overrides."specific-model-id"] +# Model-specific overrides +structured_output = true +reasoning_effort_levels = ["low", "medium", "high"] +reasoning_effort_default = "high" +``` + +## 7. Standard Tests + +LangChain provides a standard test suite for chat models via the `langchain-tests` package. Providers must implement unit and integration tests by inheriting the base test classes. + +### Unit Tests + +Create `tests/unit_tests/chat_models/test_standard.py` (reference: `/libs/standard-tests/README.md`): + +```python +"""Standard LangChain interface tests for ChatProviderModel.""" + +from typing import Type + +import pytest +from langchain_core.language_models import BaseChatModel +from langchain_tests.unit_tests import ChatModelUnitTests + +from langchain_provider_name import ChatProviderModel + + +class TestProviderModelStandard(ChatModelUnitTests): + """Standard unit tests for ChatProviderModel.""" + + @pytest.fixture + def chat_model_class(self) -> Type[BaseChatModel]: + return ChatProviderModel + + @pytest.fixture + def chat_model_params(self) -> dict: + """Parameters to instantiate the chat model. + + Must include all required constructor arguments (e.g., api_key if it's required). + """ + return { + "model": "model-123", + "api_key": "test-key", # Use environment variable in real tests + } +``` + +**Configurable test fixtures** (from `langchain-tests` README): + +- `chat_model_class` (required): The `BaseChatModel` subclass to test +- `chat_model_params`: Kwargs for instantiation (defaults to empty dict) +- `chat_model_has_tool_calling`: Whether the model supports `bind_tools()` (auto-detected) +- `chat_model_has_structured_output`: Whether the model supports `with_structured_output()` (auto-detected) + +### Integration Tests + +Create `tests/integration_tests/chat_models/test_standard.py`: + +```python +"""Standard integration tests for ChatProviderModel.""" + +from typing import Type + +import pytest +from langchain_core.language_models import BaseChatModel +from langchain_tests.integration_tests import ChatModelIntegrationTests + +from langchain_provider_name import ChatProviderModel + + +class TestProviderModelIntegration(ChatModelIntegrationTests): + """Standard integration tests for ChatProviderModel.""" + + @pytest.fixture + def chat_model_class(self) -> Type[BaseChatModel]: + return ChatProviderModel + + @pytest.fixture + def chat_model_params(self) -> dict: + """Live API credentials (loaded from environment).""" + return { + "model": "model-123", + # API key loaded from PROVIDER_NAME_API_KEY environment variable + } +``` + +### Test Coverage + +The standard test suite validates: + +- **Invoke/stream methods**: Both sync and async +- **Message handling**: All message types and content blocks +- **Tool calling**: If `bind_tools()` is implemented +- **Structured output**: If `with_structured_output()` is implemented +- **Callbacks**: Token counting, error handling +- **Model profile**: Presence and validity + +## 8. Error Handling + +Map provider-specific exceptions to LangChain's unified exception hierarchy (reference: `repo://libs/core/langchain_core/exceptions.py`): + +| Provider Exception | LangChain Exception | +|---|---| +| `ProviderAPIError` | `ModelAPIError` | +| `ProviderAuthenticationError` | `ModelAuthenticationError` | +| `ProviderRateLimitError` | `ModelRateLimitError` | +| `ProviderTimeoutError` | `ModelTimeoutError` | +| `ProviderConnectionError` | `ModelConnectionError` | + +**Implementation pattern:** + +```python +def _generate(self, messages, **kwargs): + try: + response = self._client.chat.create(...) + except provider_sdk.AuthenticationError as e: + raise ModelAuthenticationError(str(e)) from e + except provider_sdk.RateLimitError as e: + raise ModelRateLimitError(str(e)) from e + except provider_sdk.APIError as e: + raise ModelAPIError(str(e)) from e + # ... rest of generation logic +``` + +## 9. Example: OpenAI Provider Reference + +The OpenAI provider (`repo://libs/partners/openai/langchain_openai/chat_models/base.py`) is a comprehensive reference implementation demonstrating: + +- **Message conversion**: Support for images, function calling, reasoning content +- **Streaming**: Proper delta extraction and token counting +- **Tool calling**: Convert to OpenAI format, parse structured responses +- **Structured output**: JSON Schema validation and parsing +- **Error mapping**: Detailed provider-specific error handling +- **Async support**: Full async/await implementation for all methods + +## 10. Maintenance and Updates + +### Dependency Updates + +Keep the provider SDK locked in `pyproject.toml` to prevent breaking changes. Review provider release notes regularly for new models and API changes. + +### Model Profile Updates + +Run the CLI tool periodically to fetch new models from models.dev: + +```bash +langchain-model-profiles refresh --provider provider_name --data-dir ./langchain_provider_name/data +``` + +### Testing + +Run standard tests before releasing updates: + +```bash +# Unit tests (no API credentials required) +pytest tests/unit_tests/ + +# Integration tests (requires provider API credentials) +pytest tests/integration_tests/ +``` + +## Checklist for Adding a New Provider + +- [ ] Create package structure in `/libs/partners/provider_name/` +- [ ] Implement `ChatProviderModel` inheriting `BaseChatModel` +- [ ] Implement `_generate` method for synchronous generation +- [ ] Implement `_stream` method for token streaming +- [ ] Implement `_agenerate` or `_astream` for async support +- [ ] Convert messages from LangChain format to provider API schema +- [ ] Parse and convert provider responses to `AIMessage`/`AIMessageChunk` +- [ ] Implement `bind_tools()` for function calling (if supported) +- [ ] Implement `with_structured_output()` for structured output (if supported) +- [ ] Map provider exceptions to LangChain exception hierarchy +- [ ] Fetch and store model profiles via `langchain-model-profiles` CLI +- [ ] Add provider to `_BUILTIN_PROVIDERS` registry in `init_chat_model` +- [ ] Create unit test suite inheriting `ChatModelUnitTests` +- [ ] Create integration test suite inheriting `ChatModelIntegrationTests` +- [ ] Document public API in docstrings and README +- [ ] Add provider to model name inference heuristics (if applicable) +- [ ] Update integrations documentation and changelog + +## Related Documentation + +- [Chat Models Interface](/openwiki/chat-models.md) - Core chat model protocol +- [Message Types](/openwiki/messages.md) - Message abstraction and content blocks +- [Model Initialization](/openwiki/model-initialization.md) - `init_chat_model` factory details +- [OpenAI Provider](/openwiki/openai-provider.md) - Reference implementation +- [LangChain Integrations Documentation](https://docs.langchain.com/oss/python/integrations/providers/overview) - User-facing guide diff --git a/openwiki/prompts.md b/openwiki/prompts.md new file mode 100644 index 0000000000..d3e917a02e --- /dev/null +++ b/openwiki/prompts.md @@ -0,0 +1,596 @@ +--- +type: "Concept" +title: "Prompt Templates and Few-Shot Learning" +description: "Prompt templates define message sequences and variable substitution patterns for chat models. Few-shot learning selects examples dynamically to teach models by example." +tags: [prompt, template, few-shot, example-selection, variable-substitution, structured-output] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-1f4e0a5b877db4f050f2a34c + resource: repo://libs/core/langchain_core/example_selectors/base.py + - id: openwiki-source-d533a177a8d9a5dd46f561d9 + resource: repo://libs/core/langchain_core/example_selectors/length_based.py + - id: openwiki-source-5e027af8cc764d2750129cf1 + resource: repo://libs/core/langchain_core/example_selectors/semantic_similarity.py + - id: openwiki-source-03d7415879ed05a392edd62d + resource: repo://libs/core/langchain_core/prompts/base.py + - id: openwiki-source-15fdd645c1ee76ae559799c1 + resource: repo://libs/core/langchain_core/prompts/chat.py + - id: openwiki-source-bc32774051e0e8a931a6fecd + resource: repo://libs/core/langchain_core/prompts/few_shot.py + - id: openwiki-source-5549894302ea4dfd5b8f4278 + resource: repo://libs/core/langchain_core/prompts/prompt.py + - id: openwiki-source-cf81d0ba0a387a7cd9b5dfb8 + resource: repo://libs/core/langchain_core/prompts/string.py + - id: openwiki-source-204b5e61a019044332bd2dd4 + resource: repo://libs/core/langchain_core/prompts/structured.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +LangChain's **prompt templating system** provides a flexible, composable way to construct messages for language models. Prompts accept input variables, format them into message sequences, and optionally parse structured output. The system distinguishes between **string templates** (for raw text) and **chat templates** (sequences of typed messages). **Few-shot prompt templates** add the capability to select and inject examples dynamically, teaching models by demonstration. + +## Fundamental Concepts + +### Prompt Types + +LangChain provides two main categories of prompts: + +#### PromptTemplate (StringPromptTemplate) + +A `PromptTemplate` wraps a single string template with variable placeholders. The template is formatted using one of three engines: + +- **f-string** (default): Python f-string syntax. Fast, supports arbitrary expressions in `{...}` brackets with proper escaping via `{{` and `}}`. +- **mustache**: Mustache syntax using `{{variable}}`. Safer for user-controlled templates. +- **jinja2**: Full Jinja2 templating. Supports conditionals, loops, and filters, but poses security risks if templates come from untrusted sources; LangChain uses `SandboxedEnvironment` by default for defense-in-depth. + +**Key properties:** +- `template`: The template string. +- `input_variables`: List of variable names that must be provided during formatting. +- `partial_variables`: Pre-filled variables; reduce required inputs when formatting. +- `template_format`: Which engine to use (`f-string`, `mustache`, or `jinja2`). + +```python +from langchain_core.prompts import PromptTemplate + +# Simple f-string prompt +prompt = PromptTemplate.from_template("Tell me about {topic}") +output = prompt.format(topic="machine learning") + +# Jinja2 with conditionals +prompt = PromptTemplate( + template="{% if detailed %}Detailed:{% endif %} {content}", + template_format="jinja2", + input_variables=["content"], + partial_variables={"detailed": True} +) + +# Partial variables reduce required inputs +prompt = PromptTemplate( + template="User: {name}, Topic: {topic}", + input_variables=["topic"], + partial_variables={"name": "Alice"} +) +result = prompt.format(topic="AI") # name is already set +``` + +#### ChatPromptTemplate + +A `ChatPromptTemplate` sequences message prompt templates into a conversation structure. Each message has a role (system, human, ai, tool, etc.) and content. This aligns with the message-based API of chat models like GPT-4 and Claude. + +**Constructor patterns:** + +```python +from langchain_core.prompts import ChatPromptTemplate + +# Tuple shorthand +template = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful assistant."), + ("human", "Hello, {name}"), + ("ai", "Hi {name}! How can I help?"), + ("human", "{user_input}"), +]) + +# Or direct list construction +template = ChatPromptTemplate([ + ("system", "You are a helpful assistant."), + ("human", "Hello, {name}"), +]) +``` + +Supported message types in the shorthand syntax: +- `"system"` → `SystemMessagePromptTemplate` +- `"human"` → `HumanMessagePromptTemplate` +- `"ai"` → `AIMessagePromptTemplate` +- `"user"` → Alias for `"human"` +- `"assistant"` → Alias for `"ai"` +- `"tool"` / `"function"` → `ToolMessagePromptTemplate` / `FunctionMessagePromptTemplate` +- `"placeholder"` → `MessagesPlaceholder` for dynamic message lists + +**Key methods:** +- `format_messages(**kwargs)`: Returns a list of `BaseMessage` objects. +- `invoke(dict)`: Runnable interface, returns `ChatPromptValue` containing formatted messages. +- `format(**kwargs)`: Converts message list to a single string (useful for debugging or non-chat APIs). + +#### MessagesPlaceholder + +A `MessagesPlaceholder` injects a pre-formatted list of messages at a specific point in the prompt. This is essential for maintaining conversation history. + +```python +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + +template = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful assistant."), + MessagesPlaceholder("chat_history", optional=True), # optional=True allows empty list + ("human", "{question}"), +]) + +# Pass conversation history +result = template.invoke({ + "chat_history": [ + ("human", "What's 2+2?"), + ("ai", "4"), + ], + "question": "And 3+3?", +}) +# Messages: [system, human, ai, human] +``` + +The `optional=True` flag allows the placeholder to be omitted from inputs; if not provided, an empty list is substituted. The `n_messages` parameter limits how many recent messages are included (useful for token budgets). + +### Template Variable Substitution + +Prompts automatically detect variable names from template syntax and require them at format time. + +```python +from langchain_core.prompts import PromptTemplate + +prompt = PromptTemplate.from_template("Q: {question}\nA: {answer}") +print(prompt.input_variables) # ['answer', 'question'] + +# Variables are validated at runtime +try: + prompt.format(question="What?") # Missing 'answer' +except KeyError as e: + print(f"Error: {e}") +``` + +**Partial application** pre-fills some variables, reducing the required input set: + +```python +prompt = PromptTemplate.from_template("User: {name}, Question: {question}") +partial_prompt = prompt.partial(name="Bob") # Bind 'name' +output = partial_prompt.format(question="How are you?") +# Only 'question' is required now +``` + +When a prompt has exactly **one** input variable, the template can accept a non-dict argument directly: + +```python +template = ChatPromptTemplate.from_messages([ + ("system", "You are a bot."), + ("human", "{input}"), +]) +result = template.invoke("Hello!") # Auto-wraps as {"input": "Hello!"} +``` + +## Few-Shot Prompt Templates + +Few-shot learning teaches models by providing input-output examples before the user's actual query. LangChain provides two patterns: one for string prompts and one for chat-based prompts. + +### FewShotPromptTemplate + +`FewShotPromptTemplate` formats examples into a single string prompt. + +**Structure:** +``` +[prefix] + +[formatted example 1] + +[formatted example 2] + +... + +[suffix] +``` + +**Components:** +- `prefix`: Text before examples (optional). +- `example_prompt`: A `PromptTemplate` specifying how each example is formatted. +- `examples` or `example_selector`: Source of examples (either a fixed list or dynamic selector). +- `suffix`: Text after examples. Usually contains the actual task and placeholders for the new input. +- `example_separator`: String joining prefix, examples, and suffix (default: `"\n\n"`). + +```python +from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate + +examples = [ + {"input": "happy", "output": "sad"}, + {"input": "tall", "output": "short"}, +] + +example_prompt = PromptTemplate( + template="Input: {input}\nOutput: {output}", + input_variables=["input", "output"], +) + +prompt = FewShotPromptTemplate( + examples=examples, + example_prompt=example_prompt, + suffix="Input: {input}\nOutput:", + input_variables=["input"], +) + +output = prompt.format(input="big") +# Output: +# Input: happy +# Output: sad +# +# Input: tall +# Output: short +# +# Input: big +# Output: +``` + +### FewShotChatMessagePromptTemplate + +`FewShotChatMessagePromptTemplate` embeds examples as message pairs within a chat sequence. + +```python +from langchain_core.prompts import ( + ChatPromptTemplate, + FewShotChatMessagePromptTemplate, +) + +examples = [ + {"input": "2+2", "output": "4"}, + {"input": "2+3", "output": "5"}, +] + +example_prompt = ChatPromptTemplate.from_messages([ + ("human", "What is {input}?"), + ("ai", "{output}"), +]) + +few_shot = FewShotChatMessagePromptTemplate( + examples=examples, + example_prompt=example_prompt, +) + +template = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful math tutor."), + few_shot, + ("human", "What is {input}?"), +]) + +result = template.invoke({"input": "4+4"}) +# Messages: [system, human(2+2?), ai(4), human(2+3?), ai(5), human(4+4?)] +``` + +## Example Selectors + +Instead of using a fixed list of examples, an **example selector** dynamically picks relevant examples based on the input. This optimizes prompt length and relevance. + +### BaseExampleSelector Interface + +All selectors implement: + +```python +class BaseExampleSelector: + def add_example(self, example: dict[str, str]) -> Any: + """Add a new example to the store.""" + + def select_examples(self, input_variables: dict[str, str]) -> list[dict[str, Any]]: + """Select which examples to use based on inputs.""" +``` + +### SemanticSimilarityExampleSelector + +Embeds examples and input into a vector space, retrieving the `k` most similar examples. Requires a `VectorStore` and embeddings model. + +```python +from langchain_core.example_selectors import SemanticSimilarityExampleSelector +from langchain_core.embeddings import OpenAIEmbeddings +from langchain_core.vectorstores import Chroma +from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate + +examples = [ + {"input": "happy", "output": "sad"}, + {"input": "tall", "output": "short"}, + {"input": "energetic", "output": "lethargic"}, + {"input": "sunny", "output": "gloomy"}, +] + +# Create vector store from example texts +to_vectorize = [" ".join(example.values()) for example in examples] +embeddings = OpenAIEmbeddings() +vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples) + +selector = SemanticSimilarityExampleSelector( + vectorstore=vectorstore, + k=2, # Always return 2 examples +) + +example_prompt = PromptTemplate( + template="Input: {input}\nOutput: {output}", + input_variables=["input", "output"], +) + +prompt = FewShotPromptTemplate( + example_selector=selector, + example_prompt=example_prompt, + suffix="Input: {input}\nOutput:", + input_variables=["input"], +) + +# When formatting, the selector retrieves the 2 most similar examples to "bright" +output = prompt.format(input="bright") +``` + +**Key parameters:** +- `vectorstore`: VectorStore containing embedded examples. +- `k`: Number of examples to return (default: 4). +- `input_keys`: Optional filter to use only specific keys for similarity search (e.g., only the "input" field, not "output"). +- `example_keys`: Optional filter to include only certain keys in returned examples. +- `vectorstore_kwargs`: Extra arguments passed to the vectorstore's `similarity_search` method. + +### LengthBasedExampleSelector + +Selects examples greedily up to a maximum token/word count, preventing prompt length overflow. Useful when token budgets are tight. + +```python +from langchain_core.example_selectors import LengthBasedExampleSelector +from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate + +examples = [ + {"input": "happy", "output": "sad"}, + {"input": "tall", "output": "short"}, + {"input": "energetic", "output": "lethargic"}, +] + +example_prompt = PromptTemplate( + template="Input: {input}\nOutput: {output}", + input_variables=["input", "output"], +) + +selector = LengthBasedExampleSelector( + examples=examples, + example_prompt=example_prompt, + max_length=50, # Limit prompt to ~50 words + get_text_length=lambda x: len(x.split()), # Custom length function +) + +prompt = FewShotPromptTemplate( + example_selector=selector, + example_prompt=example_prompt, + suffix="Input: {input}\nOutput:", + input_variables=["input"], +) + +# Selector returns only as many examples as fit within max_length +output = prompt.format(input="fast") +``` + +**Key parameters:** +- `examples`: List of all available examples. +- `max_length`: Maximum prompt length (tokens or words, determined by `get_text_length`). +- `get_text_length`: Function to measure prompt length; defaults to word count via regex. + +**Behavior:** Examples are iterated in order; the selector stops adding when the next example would exceed `max_length`. This is greedy, not optimal, but fast and predictable. + +## Structured Output Prompts + +The `StructuredPrompt` (beta) combines a `ChatPromptTemplate` with a Pydantic schema, enabling the model to produce JSON output matching a specific schema. + +```python +from pydantic import BaseModel +from langchain_core.prompts import StructuredPrompt + +class QuestionAnswer(BaseModel): + question: str + answer: str + +template = StructuredPrompt.from_messages_and_schema( + messages=[ + ("system", "You are a helpful assistant."), + ("human", "{input}"), + ], + schema=QuestionAnswer, +) + +# When invoked with a model supporting structured output, +# the model is instructed to return JSON matching QuestionAnswer +result = template.invoke({"input": "What is LangChain?"}) +``` + +This is useful for tasks requiring consistent, parseable output (e.g., fact extraction, data classification). + +## Runnable Interface and Chaining + +All prompts inherit from `RunnableSerializable`, making them compatible with LangChain's chain-building system. + +```python +from langchain_core.prompts import ChatPromptTemplate +from langchain_openai import ChatOpenAI +from langchain_core.output_parsers import StrOutputParser + +template = ChatPromptTemplate.from_messages([ + ("system", "You are a poet."), + ("human", "Write a poem about {topic}"), +]) + +model = ChatOpenAI(model="gpt-4o") +parser = StrOutputParser() + +# Chain: prompt → model → parser +chain = template | model | parser + +result = chain.invoke({"topic": "the internet"}) +print(result) +``` + +**Key methods:** +- `invoke(dict) -> PromptValue`: Synchronous formatting. +- `ainvoke(dict) -> PromptValue`: Asynchronous formatting. +- `stream(dict)`: Streaming mode (rarely used for prompts, more common downstream). +- `batch(list[dict])`: Batch formatting multiple inputs. + +## Prompt Composition + +Prompts compose via the `+` operator, merging messages and variables. + +```python +from langchain_core.prompts import ChatPromptTemplate + +system = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful assistant. Your name is {bot_name}."), +]) + +conversation = ChatPromptTemplate.from_messages([ + ("human", "{user_input}"), +]) + +combined = system + conversation +# Equivalent to: +# ChatPromptTemplate([ +# ("system", "You are a helpful assistant. Your name is {bot_name}."), +# ("human", "{user_input}"), +# ]) + +result = combined.invoke({"bot_name": "Alice", "user_input": "Hello!"}) +``` + +**Rules:** +- When combining `ChatPromptTemplate` instances, messages are concatenated. +- Input variables from both templates are merged. +- Partial variables are merged; conflicting keys raise an error. +- Templates must have compatible formats (both f-string, both mustache, etc.). + +## Prompt Loading from Files + +**Note:** Prompt serialization and loading via the old `save()` / `load_prompt_from_config()` API is deprecated in favor of using `dumpd()` / `loads()` from `langchain_core.load`. + +To load a prompt from a JSON or YAML file, use the modern LangChain serialization API: + +```python +from langchain_core.load import loads +import json + +with open("prompt.json") as f: + prompt_dict = json.load(f) + +prompt = loads(prompt_dict) +# Returns a deserialized PromptTemplate or ChatPromptTemplate +``` + +Prompts can be serialized to JSON using `dumpd()` from `langchain_core.load`, enabling version control and sharing: + +```python +from langchain_core.load import dumpd +from langchain_core.prompts import ChatPromptTemplate + +prompt = ChatPromptTemplate.from_messages([ + ("system", "You are helpful."), + ("human", "{input}"), +]) + +prompt_dict = dumpd(prompt) +# Contains nested structure compatible with loads() +``` + +## Integration with Agent Factory + +Prompts are a core input to the **Agent Factory** (`create_agent`), providing the conversational context for agent reasoning and tool use. + +```python +from langchain.agents import create_agent +from langchain_core.prompts import ChatPromptTemplate + +system_template = ChatPromptTemplate.from_messages([ + ("system", "You are a helpful weather assistant."), +]) + +# Or use a simple string +agent = create_agent( + model="openai:gpt-4o", + tools=[weather_tool], + system_prompt="You are a helpful weather assistant." +) +``` + +The agent factory internally compiles prompts with the model and tool bindings, managing message flow through the state machine. Middleware can intercept and modify prompts before model invocation via the `wrap_model_call` hook, enabling use cases like prompt optimization or safety filters. + +## Security Considerations + +### Template Injection + +When constructing prompts from user input, use **partial variables** or **input variables** instead of string concatenation: + +```python +# UNSAFE: Vulnerable to prompt injection +user_input = input("Enter text: ") +template = f"User said: {user_input}" # Don't do this + +# SAFE: Use variable substitution +from langchain_core.prompts import PromptTemplate +prompt = PromptTemplate.from_template("User said: {user_input}") +output = prompt.format(user_input=user_input) +``` + +### Jinja2 Sandboxing + +When using Jinja2 templates, LangChain applies `SandboxedEnvironment` by default, blocking access to dunder attributes (`__class__`, `__globals__`, etc.). However: + +- **Do not accept Jinja2 templates from untrusted sources.** Sandboxing is best-effort, not foolproof. +- Regular method calls and attribute access are still allowed (e.g., `obj.method()`). +- If you must use Jinja2, prefer `f-string` or `mustache` for untrusted inputs. + +```python +# Safe: f-string template from user, validated at construction time +from langchain_core.prompts import PromptTemplate +prompt = PromptTemplate( + template="Hello {name}", # User-provided, but simple variable syntax + input_variables=["name"], +) +``` + +## Lifecycle and State + +Prompt templates are **immutable** in the functional sense: calling `format()` or `invoke()` does not mutate the template. Methods like `partial()` return new instances. + +```python +original = PromptTemplate.from_template("Say {text}") +partial = original.partial(text="hello") # Returns a NEW PromptTemplate + +# original is unchanged +print(original.input_variables) # ['text'] +print(partial.input_variables) # [] +``` + +This immutability enables safe composition and caching in pipelines. + +## Observability and Tracing + +All prompts support LangChain's standard tracing and observability hooks: + +```python +template = ChatPromptTemplate.from_messages([ + ("system", "You are helpful."), + ("human", "{input}"), +]) + +# Add metadata for tracing +template_with_metadata = template.with_config({ + "metadata": {"version": "1.0"}, + "tags": ["important"], +}) + +result = template_with_metadata.invoke({"input": "hello"}) +# The invoke is traced with the given metadata +``` + +Metadata and tags are propagated to LangSmith and other observability backends, enabling debugging and performance analysis. diff --git a/openwiki/quickstart.md b/openwiki/quickstart.md new file mode 100644 index 0000000000..9c3d40e1b3 --- /dev/null +++ b/openwiki/quickstart.md @@ -0,0 +1,431 @@ +--- +type: "Getting Started" +title: "LangChain Repository Quick Start" +description: "Entry point for engineers: orient to the monorepo structure, run first tests, understand what to edit for common tasks, and route to major development areas." +tags: [quickstart, getting-started, monorepo, setup, development, first-steps, cli-reference] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-4d1645cb6317345817452838 + resource: repo://.pre-commit-config.yaml + - id: openwiki-source-8e384445ccfbaf00747b3e18 + resource: repo://libs/core/langchain_core/__init__.py + - id: openwiki-source-96c73d2c05223b0b46abdbe9 + resource: repo://libs/core/langchain_core/callbacks/__init__.py + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-0f6ea1dd09fb4675ff4112b1 + resource: repo://libs/core/langchain_core/messages/__init__.py + - id: openwiki-source-e7908d069731cffec228727e + resource: repo://libs/core/langchain_core/output_parsers/__init__.py + - id: openwiki-source-c46a1d181ab64e61460c84c6 + resource: repo://libs/core/langchain_core/prompts/__init__.py + - id: openwiki-source-65071982a626569c8820a34b + resource: repo://libs/core/langchain_core/runnables/__init__.py + - id: openwiki-source-7c4bed110359f4f5c7847c8b + resource: repo://libs/core/langchain_core/tools/__init__.py + - id: openwiki-source-8f1875229ad4a704c8e20a06 + resource: repo://libs/core/Makefile + - id: openwiki-source-3486a94e6eb23a78271a5bfb + resource: repo://libs/core/pyproject.toml + - id: openwiki-source-47db752fe27393d5d4825827 + resource: repo://libs/langchain_v1/langchain/__init__.py + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-07e634f5cd5f00c636010306 + resource: repo://libs/langchain_v1/langchain/agents/middleware/__init__.py + - id: openwiki-source-b09b1477098d69af6abaa5b4 + resource: repo://libs/langchain_v1/langchain/chat_models/__init__.py + - id: openwiki-source-49fbcc45434b619b68220bf9 + resource: repo://libs/Makefile + - id: openwiki-source-1e66a9da38565f8901e651f4 + resource: repo://libs/partners/openai/langchain_openai/__init__.py + - id: openwiki-source-48ce5ee900993294d349b4e8 + resource: repo://libs/standard-tests/langchain_tests/__init__.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Welcome to LangChain Development + +LangChain is the agent engineering platform—a framework for building LLM-powered applications with composable abstractions, provider integrations, and orchestration primitives. This page guides you through the monorepo structure, essential setup, common dev tasks, and routing to deeper documentation. + +**New to the repo?** Start with [Installation & Setup](#installation--setup), then jump to [Quick Navigation](#quick-navigation-to-major-areas) to find what you need to work on. + +## Monorepo Overview + +LangChain is organized as a **three-layer architecture** in `/libs/`: + +``` +/libs/ +├── core/ # langchain-core: Base abstractions (Runnable, BaseChatModel, tools, prompts, messages) +├── langchain_v1/ # langchain: Agent orchestration, factory, middleware +├── partners/ # Provider-specific integrations (OpenAI, Anthropic, Ollama, etc.) +├── standard-tests/ # Shared test suites for component conformance +├── text-splitters/ # Text splitting utilities +├── model-profiles/ # LLM metadata and capability profiles +└── Makefile # Monorepo-level build targets +``` + +### When to Edit Each Layer + +| Layer | Edit when you are... | Key files | +|-------|----------------------|-----------| +| **core** | Adding or modifying base abstractions, core interfaces (Runnable, BaseChatModel, messages, tools, prompts), or callbacks. | `libs/core/langchain_core/` | +| **langchain_v1** | Building agent factory features, middleware, model initialization, chat model selection, or high-level orchestration. | `libs/langchain_v1/langchain/agents/`, `libs/langchain_v1/langchain/chat_models/` | +| **partners/{name}** | Adding a new LLM provider (OpenAI, Anthropic, etc.), model-specific features, or provider integrations. | `libs/partners/{provider}/` | + +## Installation & Setup + +### Clone the Repository + +```bash +git clone https://github.com/langchain-ai/langchain.git +cd langchain +``` + +### Install Dependencies with `uv` + +The monorepo uses `uv` for fast, deterministic dependency resolution. Install it once: + +```bash +# macOS / Linux +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Windows +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" + +# Or via Homebrew +brew install uv +``` + +Then sync all dependencies in your package: + +```bash +# From any libs/ subdirectory, install all groups (test, lint, type, dev) +uv sync --all-groups + +# Or install only what you need +uv sync --group test # For running tests +uv sync --group lint # For ruff/mypy +``` + +### Pre-Commit Hooks + +Install git hooks to enforce code quality automatically: + +```bash +pre-commit install + +# To manually run all hooks +pre-commit run --all-files + +# To run a specific hook +pre-commit run ruff --all-files +``` + +Pre-commit hooks run: +- YAML/TOML syntax validation +- Text normalization and trailing whitespace fixes +- **Per-package formatting and linting** (ruff, mypy) +- **Version consistency checks** across `pyproject.toml` files + +## Common Development Tasks + +### Run Unit Tests + +```bash +# From any package directory (libs/core, libs/langchain_v1, etc.) +make test + +# Run a specific test file +make test TEST_FILE=tests/unit_tests/agents/test_factory.py + +# Run tests in watch mode (auto-rerun on file changes) +make test_watch + +# Run extended tests (marked @pytest.mark.requires) +make extended_tests +``` + +**Key details:** +- Tests run with **socket restrictions** (`--disable-socket`) to prevent accidental network calls +- Tests run **in parallel** via pytest-xdist (`-n auto`) +- LangSmith tracing variables are unset to keep tests isolated +- Typical test path mirrors source: `langchain_core/runnables/base.py` → `tests/unit_tests/runnables/test_base.py` + +### Format and Lint + +```bash +# Format all Python files (ruff) +make format + +# Check linting issues (ruff, mypy) +make lint + +# Type checking only (mypy) +make type + +# Format only changed files (git diff against main) +make format_diff +``` + +**Tools used:** +- **ruff**: Fast Python linter and formatter (replaces black, isort, flake8) +- **mypy**: Static type checker +- Both are run via `uv run --group lint` + +### Full Local Validation + +Run this before pushing a PR: + +```bash +# From your package directory +make format && make lint && make test +``` + +Or in one line: + +```bash +cd libs/core && make format lint test +``` + +## Quick Navigation to Major Areas + +Use the table below to route to detailed documentation: + +| Task | Start Here | Key Concepts | +|------|-----------|--------------| +| **Build an agent** | [Agent Factory](/openwiki/agent-factory.md) | create_agent, AgentState, middleware composition, graph execution | +| **Add a new LLM provider** | [Adding a Chat Model Provider](/openwiki/partner-pattern.md) | ChatModel impl, message conversion, provider registration, standard tests | +| **Understand the architecture** | [Architecture Overview](/openwiki/architecture.md) | Three-layer design, dependency flow, core vs. orchestration vs. partners | +| **Work with chat models** | [Chat Model Interface](/openwiki/chat-models.md) | BaseChatModel protocol, streaming, tool binding, structured output | +| **Initialize models dynamically** | [Model Initialization](/openwiki/model-initialization.md) | init_chat_model factory, provider:model syntax, fallback chains | +| **Compose components (chains, pipelines)** | [Runnables & Composability](/openwiki/runnables.md), [Composability](/openwiki/composability.md) | Runnable protocol, \| operator, branching, retry, fallback | +| **Work with tools** | [Tools](/openwiki/tools.md) | BaseTool, schema generation, tool calling, result handling | +| **Stream responses** | [Streaming](/openwiki/streaming.md) | Token-by-token output, streaming across components | +| **Enforce response formats** | [Structured Output](/openwiki/structured-output.md) | JSON schemas, response validation, typed outputs | +| **Write middleware** | [Agent Middleware](/openwiki/middleware.md) | Middleware types, composition, custom hooks | +| **Trace agent execution** | [Agent Execution Flow](/openwiki/agent-execution.md) | Runtime lifecycle, loop control, state transitions | +| **Add observability** | [Callbacks & Tracing](/openwiki/callbacks.md) | Callback manager, LangSmith integration, logging | +| **Write unit/integration tests** | [Unit Testing](/openwiki/unit-tests.md), [Integration Testing](/openwiki/integration-tests.md) | Test structure, fixtures, mocking, VCR cassettes | +| **Work with prompts** | [Prompts](/openwiki/prompts.md) | Templates, few-shot, variables, image handling | +| **Understand message types** | [Messages](/openwiki/messages.md) | AIMessage, ToolMessage, content blocks, provider conversion | +| **Use Model Context Protocol** | [MCP Integration](/openwiki/mcp-integration.md) | MCP servers, tool adapters, elicitation | +| **Reference all file paths** | [Source Map](/openwiki/source-map.md) | Concept-to-path lookup table, directory structure | +| **Check CI/CD workflows** | [CI/CD Workflows](/openwiki/ci-workflows.md) | GitHub Actions, testing, linting, release process | +| **Development command reference** | [Dev Commands](/openwiki/dev-commands.md) | Detailed make targets, uv syntax, env setup | + +## Repository Structure at a Glance + +### Root Level + +``` +/ +├── .github/ # GitHub Actions workflows (CI/CD) +├── .pre-commit-config.yaml # Pre-commit hooks definition +├── .vscode/ # VS Code settings +├── libs/ # Main monorepo workspace +├── AGENTS.md # Agent-focused documentation +├── CLAUDE.md # Contributing guide (READ THIS BEFORE PR) +└── README.md # Top-level project overview +``` + +### Inside `/libs/` + +**core/** — Base abstractions (langchain-core package) +``` +core/ +├── langchain_core/ +│ ├── language_models/ # BaseChatModel and language model contracts +│ ├── messages/ # Message types and content blocks +│ ├── runnables/ # Runnable protocol and operators +│ ├── tools/ # BaseTool and tool utilities +│ ├── prompts/ # Prompt templates and few-shot +│ ├── callbacks/ # Callback manager and handlers +│ └── output_parsers/ # Output parsing and validation +├── tests/unit_tests/ # Unit tests (no network) +├── tests/integration_tests/ # Integration tests (live APIs) +├── Makefile # Build targets (test, lint, format) +└── pyproject.toml # Package deps and metadata +``` + +**langchain_v1/** — Agent orchestration (langchain package) +``` +langchain_v1/ +├── langchain/ +│ ├── agents/ +│ │ ├── factory.py # create_agent function +│ │ ├── middleware/ # Pluggable middleware hooks +│ │ └── structured_output.py # Response schema +│ ├── chat_models/ +│ │ └── base.py # init_chat_model factory +│ ├── mcp/ # Model Context Protocol +│ └── ... +├── tests/unit_tests/ +├── tests/integration_tests/ +├── tests/cassettes/ # VCR cassettes for HTTP mocking +├── Makefile +└── pyproject.toml +``` + +**partners/** — Provider integrations +``` +partners/ +├── openai/ # ChatOpenAI, embeddings +├── anthropic/ # ChatAnthropic (Claude) +├── ollama/ # ChatOllama (local models) +├── groq/ # ChatGroq +├── mistralai/ # ChatMistralAI +├── huggingface/ # HuggingFace models/embeddings +├── deepseek/ # ChatDeepSeek +└── ... (20+ more providers) +``` + +Each partner has the same structure: +``` +provider/ +├── langchain_{provider}/ +│ ├── __init__.py # Exports ChatModel class +│ ├── chat_models/ +│ │ └── base.py # ChatModel implementation +│ └── data/ # Model profiles +├── tests/ +│ ├── unit_tests/ # Standard tests + custom +│ └── integration_tests/ +├── pyproject.toml +├── Makefile +└── uv.lock +``` + +## Your First PR: A Workflow + +### 1. Pick a Task + +Decide what you want to work on using the [Quick Navigation](#quick-navigation-to-major-areas) table above. For first-time contributors: +- **Easy**: Add a test, fix a type error, improve documentation +- **Medium**: Add a new middleware hook, extend a tool interface +- **Hard**: Add a new provider integration (follow [Adding a Chat Model Provider](/openwiki/partner-pattern.md)) + +### 2. Read the Contributing Guide + +Before coding, read: +- **[CLAUDE.md](repo://CLAUDE.md)** — Conventions, style, and PR expectations +- **Relevant wiki page** — Deep context on your area (see table above) + +### 3. Set Up Your Package + +```bash +cd libs/{core|langchain_v1|partners/provider} +uv sync --all-groups +pre-commit install +``` + +### 4. Make Your Changes + +Follow the style and patterns you see in the codebase. Use type hints; write tests alongside code. + +### 5. Run Local Checks + +```bash +make format lint test +``` + +All checks must pass before pushing. + +### 6. Commit and Push + +```bash +git add . +git commit -m "Brief description of change" +git push origin your-branch +``` + +Pre-commit hooks will run automatically. If they fail, fix and commit again. + +### 7. Open a Pull Request + +Link the PR to any relevant issue and reference the wiki pages you read in the description. The LangChain team will review and provide feedback. + +## Key Files to Know + +| File | Purpose | +|------|---------| +| `CLAUDE.md` | Contributing guide, style, and conventions | +| `libs/Makefile` | Monorepo-level make targets (lock, check-lock) | +| `libs/{core,langchain_v1,partners/*/Makefile` | Per-package test, lint, format targets | +| `.pre-commit-config.yaml` | Git hooks for code quality | +| `pyproject.toml` (per-package) | Package metadata, dependencies, build config | + +## Troubleshooting + +### Tests Fail with Socket Errors +Tests run with socket restrictions by default. If you need network access: +- Write an integration test in `tests/integration_tests/` (see [Integration Testing](/openwiki/integration-tests.md)) +- Or disable socket restrictions locally: `uv run --group test pytest --disable-socket=false ...` + +### Import Errors or Version Mismatches +Regenerate lockfiles: +```bash +cd libs +make lock +``` + +Or in a single package: +```bash +cd libs/core +uv lock +``` + +### Type Checking Fails +Run mypy to see detailed errors: +```bash +make type +``` + +Check the [Chat Models](/openwiki/chat-models.md) or [Runnables](/openwiki/runnables.md) pages for type signature patterns. + +### Pre-Commit Hooks Block Commit +Pre-commit will auto-fix formatting and some issues. Re-stage and commit: +```bash +git add . +git commit -m "..." # Try again +``` + +If linting still fails, run `make lint` to see details and fix manually. + +## Quick Command Reference + +```bash +# Setup +uv sync --all-groups # Install all dependencies +pre-commit install # Setup git hooks + +# Testing +make test # Run unit tests +make test TEST_FILE=path/ # Run specific test file +make test_watch # Watch mode (auto-rerun) +make integration_tests # Run integration tests + +# Code Quality +make format # Format code (ruff) +make lint # Check linting (ruff, mypy) +make type # Type check only (mypy) + +# Lockfile Management +cd libs && make lock # Regenerate all lockfiles +cd libs && make check-lock # Verify lockfiles are up-to-date + +# All Before PR +make format && make lint && make test +``` + +## Next Steps + +1. **Read [CLAUDE.md](repo://CLAUDE.md)** for contributing conventions +2. **Pick a wiki page** from [Quick Navigation](#quick-navigation-to-major-areas) matching your task +3. **Clone, setup, and make your first change** +4. **Run `make format lint test`** to validate locally +5. **Open a PR** and engage with the team + +Welcome to LangChain! 🚀 diff --git a/openwiki/runnables.md b/openwiki/runnables.md new file mode 100644 index 0000000000..9ed3d2687e --- /dev/null +++ b/openwiki/runnables.md @@ -0,0 +1,718 @@ +--- +type: "Concept" +title: "Runnable: Core Composition Layer" +description: "Explain the Runnable protocol and how it enables composable chaining of LLM components through the LangChain Expression Language (LCEL)." +tags: [runnable, lcel, composition, invoke, stream, batch, async, chaining] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-a1981e868973f6fd7f71e12e + resource: repo://libs/core/langchain_core/runnables/base.py + - id: openwiki-source-48e94bbe49ab4f33ba87e9cb + resource: repo://libs/core/langchain_core/runnables/branch.py + - id: openwiki-source-079792f059657900794e2955 + resource: repo://libs/core/langchain_core/runnables/config.py + - id: openwiki-source-f9f4c1dc4f9cdf80d824ce15 + resource: repo://libs/core/langchain_core/runnables/fallbacks.py + - id: openwiki-source-ebe3f825462d0b4a14ee3717 + resource: repo://libs/core/langchain_core/runnables/retry.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +**Runnable** is the fundamental abstraction in LangChain's core layer. It defines a serializable, composable interface that every LLM component—prompts, models, tools, chains, output parsers—must implement. A `Runnable` is a unit of work that transforms input into output through five core operations: synchronous invoke, asynchronous invoke, batch processing, streaming, and introspection of input/output schemas. + +The **LangChain Expression Language (LCEL)** leverages Runnables to build chains declaratively using composition operators. Any chain built from Runnables automatically inherits sync, async, batch, and streaming support without additional implementation. This unifies execution patterns and enables sophisticated control flows—sequential piping (`|`), parallel forking (`+`), branching, fallback handling, and retry logic—all composable as first-class operations. + +## Core Runnable Protocol + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L133-L265` + +The `Runnable` abstract base class defines the contract for all components. A Runnable is generic over input and output types (`Runnable[Input, Output]`) and must implement the abstract `invoke` method. All other execution methods have default implementations that subclasses can override for optimization. + +### Key Responsibilities + +1. **Invoke**: Execute a single input synchronously and return a single output. +2. **Batch**: Process multiple inputs in parallel (default uses thread pool, subclasses can optimize). +3. **Stream**: Yield partial outputs as they are produced (default calls invoke once; specialized for streaming models). +4. **Async variants**: Asynchronous versions of invoke, batch, and stream (default delegates to sync via executor; subclasses implement natively). +5. **Schema introspection**: Expose input type, output type, and configuration schema as Pydantic models for validation and tooling. +6. **Composition**: Support chaining with other Runnables via operators and methods. + +### Synchronous Methods + +**`invoke(input, config=None)`** (`repo://libs/core/langchain_core/runnables/base.py#L885-L906`) is the abstract core method: + +```python +@abstractmethod +def invoke( + self, + input: Input, + config: RunnableConfig | None = None, + **kwargs: Any, +) -> Output: + """Transform a single input into an output.""" +``` + +- **Must be implemented** by all subclasses. +- Accepts optional `RunnableConfig` for tags, metadata, callbacks, recursion limits, and configurable parameters. +- Returns a single output. + +**`batch(inputs, config=None, return_exceptions=False)`** (`repo://libs/core/langchain_core/runnables/base.py#L931-L975`) processes multiple inputs: + +```python +def batch( + self, + inputs: list[Input], + config: RunnableConfig | list[RunnableConfig] | None = None, + *, + return_exceptions: bool = False, + **kwargs: Any | None, +) -> list[Output]: + """Default implementation runs invoke in parallel using a thread pool executor.""" +``` + +- **Default**: Calls `invoke` for each input in parallel via `ThreadPoolExecutor`. +- Accepts a single config (applied to all) or a list of configs (one per input). +- If `return_exceptions=True`, exceptions are returned as-is in the output list; otherwise, they are raised. +- Subclasses override for batching-aware backends (e.g., LLMs with batch API support). + +**`batch_as_completed(inputs, config=None, return_exceptions=False)`** yields results as they complete, useful for streaming partial results while processing. + +**`stream(input, config=None)`** (`repo://libs/core/langchain_core/runnables/base.py#L1194-L1213`) yields partial outputs: + +```python +def stream( + self, + input: Input, + config: RunnableConfig | None = None, + **kwargs: Any | None, +) -> Iterator[Output]: + """Default implementation of stream, which calls invoke.""" +``` + +- **Default**: Yields one full output from `invoke`. +- **Specialized implementations** (e.g., chat models, token-streaming parsers) yield chunks as they arrive. +- Enables responsive UX by progressive output display. + +### Asynchronous Methods + +**`ainvoke(input, config=None)`** (`repo://libs/core/langchain_core/runnables/base.py#L908-L929`) is the async variant: + +```python +async def ainvoke( + self, + input: Input, + config: RunnableConfig | None = None, + **kwargs: Any, +) -> Output: + """Transform a single input into an output.""" +``` + +- **Default**: Delegates to sync `invoke` via `run_in_executor`. +- **Subclasses override** for native async (e.g., async API calls). + +**`abatch(inputs, config=None, return_exceptions=False)`** (`repo://libs/core/langchain_core/runnables/base.py#L1066-L1112`) batches async: + +```python +async def abatch( + self, + inputs: list[Input], + config: RunnableConfig | list[RunnableConfig] | None = None, + *, + return_exceptions: bool = False, + **kwargs: Any | None, +) -> list[Output]: + """Default implementation runs ainvoke in parallel using asyncio.gather.""" +``` + +- Calls `ainvoke` for each input concurrently. +- Respects `max_concurrency` from config to limit parallelism. + +**`abatch_as_completed(inputs, config=None, return_exceptions=False)`** yields completed async results as they finish. + +**`astream(input, config=None)`** (`repo://libs/core/langchain_core/runnables/base.py#L1215-L1234`) is the async streaming variant: + +```python +async def astream( + self, + input: Input, + config: RunnableConfig | None = None, + **kwargs: Any | None, +) -> AsyncIterator[Output]: + """Default implementation of astream, which calls ainvoke.""" +``` + +- **Default**: Yields one output from `ainvoke`. +- **Specialized** for streaming backends. + +### Schema Introspection + +**`input_schema` / `output_schema`** (`repo://libs/core/langchain_core/runnables/base.py#L374-L527`) expose the input and output types as Pydantic models: + +```python +@property +def input_schema(self) -> TypeBaseModel: + """The type of input this Runnable accepts specified as a Pydantic model.""" + return self.get_input_schema() + +@property +def output_schema(self) -> TypeBaseModel: + """The type of output this Runnable produces specified as a Pydantic model.""" + return self.get_output_schema() +``` + +- Inferred from generic type parameters or implementer-provided type hints. +- Can be converted to JSON Schema for API documentation, validation, and tools. + +**`config_schema(include=None)`** returns a Pydantic model for configuration fields marked as configurable via `configurable_fields()` or `configurable_alternatives()`. + +## Composition Operators and Methods + +The heart of LCEL is declarative composition. Runnables are chained together using operators and methods that create new composite Runnables. + +### Sequential Composition: Pipe Operator `|` + +**`__or__(other)` / `__ror__(other)`** (`repo://libs/core/langchain_core/runnables/base.py#L648-L722`) creates a `RunnableSequence`: + +```python +def __or__(self, other): + """Runnable "or" operator. Compose this Runnable with another to create RunnableSequence.""" + return RunnableSequence(self, coerce_to_runnable(other)) +``` + +**Example**: +```python +from langchain_core.runnables import RunnableLambda +from langchain_core.prompts import PromptTemplate +from langchain_core.output_parsers import StrOutputParser + +prompt = PromptTemplate.from_template("Tell me about {topic}") +model = ChatOpenAI() +parser = StrOutputParser() + +chain = prompt | model | parser +result = chain.invoke({"topic": "machine learning"}) +# result is a string, the parsed model output +``` + +- `other` can be another `Runnable`, a callable, a dict (coerced to `RunnableParallel`), or any `RunnableLike`. +- Automatically flattens nested `RunnableSequence` for efficiency. +- The output of the left side becomes the input to the right side. + +**`pipe(*others, name=None)`** (`repo://libs/core/langchain_core/runnables/base.py#L724-L771`) is the explicit method form: + +```python +sequence = runnable_1.pipe(runnable_2, runnable_3) +# Equivalent to: runnable_1 | runnable_2 | runnable_3 +``` + +### Parallel Composition: Fork with Dict + +**Dict literal** or **`RunnableParallel`** (`repo://libs/core/langchain_core/runnables/base.py#L3864-L3990`) invokes multiple Runnables concurrently with the same input: + +```python +from langchain_core.runnables import RunnableParallel + +# Via dict literal in a sequence +sequence = input_runnable | { + "branch_a": runnable_a, + "branch_b": runnable_b, +} + +# Explicit RunnableParallel +parallel = RunnableParallel( + result1=runnable_1, + result2=runnable_2, +) +``` + +- All runnables receive the same input. +- Results are collected into a dict with user-specified keys. +- Execution is parallel via `asyncio.gather` or thread pool. +- Useful for processing branches in parallel: multi-chain retrieval, multi-aspect analysis, etc. + +### Sequencing with RunnableSequence + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L3075-L3235` + +`RunnableSequence` is the composition engine for sequential execution. It chains multiple `Runnable` objects where each output feeds into the next input. The `first`, `middle`, and `last` attributes store the steps; the sequence automatically optimizes batch and stream operations by calling each step's batch/stream method in order. + +```python +from langchain_core.runnables import RunnableSequence + +sequence = RunnableSequence( + first=prompt, + middle=[some_runnable], + last=parser, +) + +# Equivalent to: prompt | some_runnable | parser +``` + +**Key behavior**: +- **Batching**: Each step in the sequence is called with the batch of inputs from the previous step. If a step's batch is optimized (e.g., for an LLM API), the entire chain benefits. +- **Streaming**: If all steps implement `transform` (streaming input → streaming output), the sequence streams end-to-end. Otherwise, streaming begins after the last blocking step. +- **Async**: Async variants call each step's async method in order via `asyncio.gather` for parallelizable steps within the chain. + +### Branching: RunnableBranch + +**Location**: `repo://libs/core/langchain_core/runnables/branch.py#L43-L150` + +`RunnableBranch` selects and runs one of several branches based on a condition: + +```python +from langchain_core.runnables import RunnableBranch + +branch = RunnableBranch( + (lambda x: isinstance(x, str), lambda x: x.upper()), + (lambda x: isinstance(x, int), lambda x: x + 1), + lambda x: "default", # fallback if no condition matches +) + +branch.invoke("hello") # "HELLO" +branch.invoke(42) # 43 +branch.invoke(None) # "default" +``` + +- Takes a list of `(condition, runnable)` tuples and a default runnable. +- Conditions are Runnables or callables returning bool. +- At invoke time, the first condition that returns `True` is selected; its runnable is executed on the input. +- If no condition matches, the default runnable is run. +- Conditions are evaluated sequentially; use judiciously for complex logic. + +### Routing: RouterRunnable + +**Location**: `repo://libs/core/langchain_core/runnables/router.py#L46-L150` + +`RouterRunnable` routes to a runnable by key: + +```python +from langchain_core.runnables import RouterRunnable + +router = RouterRunnable(runnables={ + "math": math_chain, + "text": text_chain, +}) + +router.invoke({"key": "math", "input": "2 + 2"}) # Uses math_chain +``` + +- Input is a dict with `"key"` (string identifying the route) and `"input"` (the actual data). +- The selected runnable processes the input. +- Useful for dispatch tables and multi-expert architectures. + +### Fallback and Retry + +**Fallbacks**: `RunnableWithFallbacks` (`repo://libs/core/langchain_core/runnables/fallbacks.py#L37-L150`) + +```python +from langchain_core.runnables import RunnableWithFallbacks + +model = ChatOpenAI().with_fallbacks([ChatAnthropic(), ChatCohere()]) +# Try ChatOpenAI first, then ChatAnthropic, then ChatCohere if prior ones fail. + +result = model.invoke("Hello") # Returns first successful result +``` + +- Executes the primary runnable. +- If it fails with an exception in `exceptions_to_handle`, tries the next fallback. +- Proceeds until one succeeds or all fail. +- Optionally passes exceptions to fallbacks for adaptive recovery. + +**Retry**: `RunnableRetry` (`repo://libs/core/langchain_core/runnables/retry.py#L48-L150`) + +```python +runnable = ChatOpenAI().with_retry( + retry_if_exception_type=(APIError,), + stop_after_attempt=3, + wait_exponential_jitter=True, +) +# Retries on APIError up to 3 times with exponential backoff + jitter. +``` + +- Uses `tenacity` for retry logic. +- Configurable stop conditions, wait strategies, and exception types. +- Best applied to individual runnables (e.g., LLM calls) rather than entire chains. + +## RunnableConfig: Threading Context + +**Location**: `repo://libs/core/langchain_core/runnables/config.py#L57-L129` + +`RunnableConfig` is a `TypedDict` that carries execution context through the chain: + +```python +class RunnableConfig(TypedDict, total=False): + tags: list[str] # For filtering runs, grouping telemetry + metadata: dict[str, Any] # Arbitrary metadata (JSON-serializable) + callbacks: Callbacks # Lifecycle handlers (on_start, on_end, on_error, etc.) + run_name: str # Name for tracing/logging + max_concurrency: int | None # Limit parallel execution + recursion_limit: int # Prevent infinite recursion (default 25) + configurable: dict[str, Any] # Runtime config overrides for configurable fields + run_id: uuid.UUID | None # Unique execution ID +``` + +- **Propagation**: Config is threaded through child runnables via context variables (`var_child_runnable_config`) and explicit parameter passing. +- **Merging**: Configs are merged when passed down (e.g., tags accumulate: parent tags + child tags). +- **Callbacks**: Attached callbacks receive hooks for all intermediate steps, enabling observability and custom logic. +- **Configurable fields**: The `configurable` dict supplies runtime values for fields marked with `configurable_fields()` or `configurable_alternatives()`, enabling dynamic behavior. + +## Core Runnable Types + +### RunnableLambda + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L4703-L4850` + +`RunnableLambda` wraps a Python callable into a `Runnable`: + +```python +from langchain_core.runnables import RunnableLambda + +def add_one(x: int) -> int: + return x + 1 + +runnable = RunnableLambda(add_one) +runnable.invoke(1) # 2 + +# Async support +async def add_one_async(x: int) -> int: + return x + 1 + +runnable = RunnableLambda(add_one, afunc=add_one_async) +await runnable.ainvoke(1) # 2 +``` + +- Ideal for wrapping custom logic, data transformations, and simple operations. +- If a lambda returns a `Runnable`, that runnable is automatically invoked. +- Does not support streaming by default (use `RunnableGenerator` for streaming lambdas). +- Automatically detects async callables and provides native async support. + +### RunnableGenerator + +Wraps generator functions (sync or async) to create streaming runnables. + +### RunnableParallel + +Already described above; runs multiple runnables concurrently on the same input and collects results into a dict. + +### RunnableMap (Alias for RunnableParallel) + +Synonymous with `RunnableParallel`. + +### RunnablePassthrough + +**Location**: `repo://libs/core/langchain_core/runnables/passthrough.py` + +Passes input through unchanged, useful in parallel branches to preserve input for later steps. + +```python +from langchain_core.runnables import RunnablePassthrough + +chain = prompt | { + "original_input": RunnablePassthrough(), + "model_output": model, +} +# Output: {"original_input": , "model_output": } +``` + +### RunnablePick + +Selects specific keys from dict output: + +```python +chain | RunnablePick("key_a") # Output only "key_a" +# Or: chain.pick(["key_a", "key_b"]) +``` + +### RunnableAssign + +Adds new fields to dict output by invoking additional runnables: + +```python +chain.assign(new_field=some_runnable) +# Output now includes original fields + new_field +``` + +## Runnable Hierarchy + +The following diagram shows the core Runnable class hierarchy: + +```mermaid +classDiagram + class Runnable { + +invoke(input, config) Output* + +ainvoke(input, config) Output + +batch(inputs, config) list[Output] + +abatch(inputs, config) list[Output] + +stream(input, config) Iterator[Output] + +astream(input, config) AsyncIterator[Output] + +property input_schema TypeBaseModel + +property output_schema TypeBaseModel + +__or__(other) RunnableSequence + +pipe(*others) RunnableSequence + +with_fallbacks(fallbacks) RunnableWithFallbacks + +with_retry(params) RunnableRetry + } + + class RunnableSerializable { + +model_rebuild() + +is_lc_serializable() bool + +get_lc_namespace() list[str] + } + + class RunnableSequence { + +first Runnable + +middle list[Runnable] + +last Runnable + +steps list[Runnable] + } + + class RunnableParallel { + +steps__ Mapping[str, Runnable] + } + + class RunnableLambda { + +func Callable + +afunc Callable + } + + class RunnableBranch { + +branches Sequence[tuple[Runnable, Runnable]] + +default Runnable + } + + class RouterRunnable { + +runnables Mapping[str, Runnable] + } + + class RunnableWithFallbacks { + +runnable Runnable + +fallbacks Sequence[Runnable] + } + + class RunnableRetry { + +bound Runnable + +max_attempt_number int + } + + Runnable <|-- RunnableSerializable + RunnableSerializable <|-- RunnableSequence + RunnableSerializable <|-- RunnableParallel + RunnableSerializable <|-- RunnableLambda + RunnableSerializable <|-- RunnableBranch + RunnableSerializable <|-- RouterRunnable + RunnableSerializable <|-- RunnableWithFallbacks + RunnableSerializable <|-- RunnableRetry +``` + +Class hierarchy of core Runnable types and their relationships. + +## Execution Flow: Invoke and Config Propagation + +The following diagram shows how a config and execution flow through a composed chain: + +```mermaid +sequenceDiagram + participant User + participant RunnableSequence + participant Step1 as Prompt + participant Step2 as ChatModel + participant Step3 as Parser + + User->>RunnableSequence: invoke(input, config) + note over RunnableSequence: merge config with defaults + RunnableSequence->>Step1: invoke(input, merged_config) + Step1->>Step1: format with input variables + Step1-->>RunnableSequence: output (formatted prompt) + RunnableSequence->>Step2: invoke(formatted_prompt, merged_config) + Step2->>Step2: call LLM API + Step2-->>RunnableSequence: output (AIMessage) + RunnableSequence->>Step3: invoke(AIMessage, merged_config) + Step3->>Step3: parse message content + Step3-->>RunnableSequence: output (parsed result) + RunnableSequence-->>User: final output + note over User,RunnableSequence: tags, metadata, callbacks are
threaded through each step +``` + +Execution flow through a sequential composition with config propagation. + +## RunnableLike and Type Coercion + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L6608-L6664` + +`RunnableLike` is a union type that accepts anything composable: + +```python +RunnableLike = ( + Runnable[Input, Output] + | Callable[[Input], Output] + | Callable[[Input], Awaitable[Output]] + | Callable[[Iterator[Input]], Iterator[Output]] + | Callable[[AsyncIterator[Input]], AsyncIterator[Output]] + | Mapping[str, Any] +) +``` + +**`coerce_to_runnable(thing)`** converts any `RunnableLike` into a `Runnable`: + +```python +def coerce_to_runnable(thing: RunnableLike) -> Runnable: + """Coerce a Runnable-like object into a Runnable.""" + if isinstance(thing, Runnable): + return thing # Already a Runnable + if is_async_generator(thing) or inspect.isgeneratorfunction(thing): + return RunnableGenerator(thing) # Wrap generators + if callable(thing): + return RunnableLambda(thing) # Wrap functions + if isinstance(thing, dict): + return RunnableParallel(thing) # Coerce dicts to parallel + raise TypeError("...") # Unsupported type +``` + +This enables the intuitive syntax: `prompt | my_function | {"field": another_function}`. Each element is automatically coerced to a Runnable. + +## Configuration and Extensibility + +### Configurable Fields + +**`configurable_fields(**fields)`** marks fields as runtime-configurable: + +```python +from langchain_core.runnables import ConfigurableField + +model = ChatOpenAI( + model="gpt-4" +).configurable_fields( + model=ConfigurableField( + id="model_name", + name="Model Name", + description="The model to use", + ) +) + +# At runtime, override the model: +result = model.invoke( + "Hello", + config={"configurable": {"model_name": "gpt-3.5-turbo"}} +) +``` + +- Fields are exposed in `config_schema()`. +- Runtime values are applied before invoke. + +### Callbacks and Tracing + +Runnables integrate with the callback system via `RunnableConfig`: + +```python +from langchain_core.tracers import ConsoleCallbackHandler + +chain.invoke( + input, + config={ + "callbacks": [ConsoleCallbackHandler()], + "tags": ["production", "query"], + "metadata": {"user_id": 123}, + } +) +``` + +Callbacks receive hooks for: +- `on_runnable_start`: When a step begins +- `on_runnable_end`: When a step completes successfully +- `on_runnable_error`: When a step fails +- `on_llm_new_token`: Token-by-token streaming +- And many others + +This enables real-time monitoring, custom logging, user attribution, and performance tracking. + +## All Components Are Runnables + +A key design principle: **everything is a Runnable**. This includes: + +- **Prompts** (`PromptTemplate`, `ChatPromptTemplate`): Format input variables into messages. +- **Chat Models** (`ChatOpenAI`, `ChatAnthropic`): Invoke LLM APIs. +- **Output Parsers** (`JsonOutputParser`, `StrOutputParser`): Parse model output into structured types. +- **Retrievers** (`VectorStoreRetriever`, `BM25Retriever`): Fetch relevant documents. +- **Tools** (`BaseTool`): Callable functions with schemas. +- **Chains**: Composite runnables built from other runnables. +- **Agents**: Orchestrate tools and runnables in feedback loops. + +Because all are Runnables, any can be composed with any other via `|`, parallelized, retried, and monitored uniformly. + +## Simple Example: Prompt → Model → Parser Chain + +```python +from langchain_core.prompts import PromptTemplate +from langchain_core.chat_models import ChatOpenAI +from langchain_core.output_parsers import JsonOutputParser + +# Define input schema +class TopicInfo(BaseModel): + topic: str + examples: list[str] + +# Create the chain +prompt = PromptTemplate.from_template( + "Provide 3 examples of {topic} in JSON format:\n" + "{format_instructions}" +) +model = ChatOpenAI(model="gpt-4") +parser = JsonOutputParser(pydantic_object=TopicInfo) + +chain = prompt | model | parser + +# Invoke +result = chain.invoke({ + "topic": "machine learning algorithms", + "format_instructions": parser.get_format_instructions(), +}) +# result is a TopicInfo instance with topic and examples + +# Batch +results = chain.batch([ + {"topic": "AI", ...}, + {"topic": "NLP", ...}, + {"topic": "Vision", ...}, +]) +# results is a list of TopicInfo instances, processed in parallel + +# Stream +for chunk in chain.stream({"topic": "Reinforcement Learning", ...}): + print(chunk) # Yields intermediate outputs as they arrive +``` + +## Key Invariants and Design Patterns + +### Serializability + +All core Runnables are serializable via LangChain's serialization system. This enables: +- Saving chains to JSON or YAML for deployment +- Reproducing chains from persisted configs +- Sharing chain definitions across services + +### Immutability and Fluent API + +Composition methods (`with_fallbacks`, `with_retry`, `pick`, `assign`, etc.) return new `Runnable` instances; they do not mutate the original. This enables safe chaining and allows reuse of components. + +### Type Transparency + +Input and output types are exposed and enforced: +- `input_schema` validates input before invoke +- `output_schema` documents expected output for tools and UIs +- JSON schema generation enables API/OpenAPI documentation + +### Lazy Execution + +Chains are constructed lazily—composing with `|` does not execute anything. Execution happens only on `invoke`, `batch`, `stream`, or async variants. + +### Streaming as a First-Class Concern + +Streaming is not an afterthought; it is a core execution mode. Every Runnable exposes `stream` and `astream`, enabling responsive and incremental output. + +### Concurrency and Parallelism + +Batch operations use thread pools or asyncio concurrency to process inputs in parallel. Config controls parallelism via `max_concurrency`. Async methods are first-class, enabling high-concurrency server deployments. diff --git a/openwiki/source-map.md b/openwiki/source-map.md new file mode 100644 index 0000000000..30f38270bf --- /dev/null +++ b/openwiki/source-map.md @@ -0,0 +1,279 @@ +--- +type: "Reference" +title: "Source Map: Repository File Organization" +description: "Quick reference for locating code by topic, mapping LangChain concepts to their implementation paths across the monorepo including core abstractions, agents, middleware, partners, and configuration files." +tags: [reference, file-organization, monorepo, codebase-map, pathfinding] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-4d1645cb6317345817452838 + resource: repo://.pre-commit-config.yaml + - id: openwiki-source-1c233fccf5a66b84d0045366 + resource: repo://libs/core/langchain_core/callbacks/manager.py + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-b32b84365d17276620c41ebc + resource: repo://libs/core/langchain_core/messages/base.py + - id: openwiki-source-03d7415879ed05a392edd62d + resource: repo://libs/core/langchain_core/prompts/base.py + - id: openwiki-source-a1981e868973f6fd7f71e12e + resource: repo://libs/core/langchain_core/runnables/base.py + - id: openwiki-source-4ff475d7b00540f962384251 + resource: repo://libs/core/langchain_core/tools/base.py + - id: openwiki-source-3486a94e6eb23a78271a5bfb + resource: repo://libs/core/pyproject.toml + - id: openwiki-source-a690cf632a02205e3f555be8 + resource: repo://libs/core/tests/unit_tests/test_tools.py + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-03e8ca0eebe37feda8566793 + resource: repo://libs/langchain_v1/langchain/agents/middleware/types.py + - id: openwiki-source-ec30ab6256dd50cc670919f6 + resource: repo://libs/langchain_v1/langchain/agents/structured_output.py + - id: openwiki-source-c479d4fffee5cf62576699e4 + resource: repo://libs/langchain_v1/langchain/chat_models/base.py + - id: openwiki-source-0a3228970b0eadc4bcadbb5d + resource: repo://libs/langchain_v1/langchain/mcp/adapter.py + - id: openwiki-source-ba4876d385d4d18ed4fa0342 + resource: repo://libs/langchain_v1/pyproject.toml + - id: openwiki-source-94d5218d78dfd52679adc96b + resource: repo://libs/langchain_v1/tests/unit_tests/test_imports.py + - id: openwiki-source-49fbcc45434b619b68220bf9 + resource: repo://libs/Makefile + - id: openwiki-source-77f5d6298c73161b4d4f697e + resource: repo://libs/model-profiles/langchain_model_profiles/__init__.py + - id: openwiki-source-738512768ef81ae009b097ac + resource: repo://libs/partners/openai/langchain_openai/chat_models/base.py + - id: openwiki-source-bd29e79613d5f366a00068f5 + resource: repo://libs/standard-tests/langchain_tests/base.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +This page provides a quick reference for locating code by topic in the LangChain monorepo. The repository is organized as a multi-package workspace with a three-layer architecture: **langchain-core** (base abstractions), **langchain** (orchestration and agents), and **partners** (provider integrations). Use this map to navigate directly to the code responsible for a given concept. + +## Concept-to-Path Mapping + +| Concept | Primary Path | Purpose | +|---------|--------------|---------| +| **Agent Factory** | `repo://libs/langchain_v1/langchain/agents/factory.py` | Constructs compiled LangGraph state machines for agentic loops with model binding, tool execution, and middleware composition | +| **Agent Middleware** | `repo://libs/langchain_v1/langchain/agents/middleware/` | Pluggable hooks for model calls, tool invocation, and lifecycle events (retries, human-in-loop, redaction, etc.) | +| **Chat Models** | `repo://libs/core/langchain_core/language_models/chat_models.py` | `BaseChatModel` abstract base and unified provider interface for streaming, batching, and token counting | +| **Chat Models (Core Interfaces)** | `repo://libs/core/langchain_core/language_models/` | Base language model classes, fake models for testing, and compatibility bridges | +| **Chat Models (Partner Implementations)** | `repo://libs/partners/*/` (e.g., `openai/`, `anthropic/`, `ollama/`) | Provider-specific implementations: ChatOpenAI, ChatAnthropic, ChatOllama, etc. | +| **Callbacks & Tracing** | `repo://libs/core/langchain_core/callbacks/` | Callback manager, base handlers, streaming output, and LangSmith integration | +| **Messages** | `repo://libs/core/langchain_core/messages/` | Message types (AIMessage, HumanMessage, SystemMessage, ToolMessage) and content blocks | +| **Model Initialization** | `repo://libs/langchain_v1/langchain/chat_models/base.py` | `init_chat_model()` factory for dynamic model discovery and loading by provider:model identifier | +| **Model Profiles** | `repo://libs/model-profiles/langchain_model_profiles/` | Metadata and profiles for LLM behavior, capabilities, and configuration | +| **MCP (Model Context Protocol)** | `repo://libs/langchain_v1/langchain/mcp/` | Adapter, tools, and elicitation for MCP-based model integrations | +| **Output Parsers** | `repo://libs/core/langchain_core/output_parsers/` | Structured output parsing, Pydantic model marshaling, and output validation | +| **Prompts** | `repo://libs/core/langchain_core/prompts/` | Chat and string prompt templates, few-shot examples, and image prompts | +| **Runnables (LCEL)** | `repo://libs/core/langchain_core/runnables/` | Foundational `Runnable[Input, Output]` protocol, composition operators, and control flow (piping, branching, fallback, retry) | +| **Structured Output** | `repo://libs/langchain_v1/langchain/agents/structured_output.py` | Schema definition and response marshaling for typed agent outputs | +| **Tools** | `repo://libs/core/langchain_core/tools/` | `BaseTool` abstraction, tool conversion from functions/Pydantic, and tool rendering | +| **Tests (Core Unit)** | `repo://libs/core/tests/unit_tests/` | Unit tests for abstractions: runnables, messages, prompts, tools, callbacks | +| **Tests (Core Integration)** | `repo://libs/core/tests/integration_tests/` | Integration tests with live providers and external services | +| **Tests (LangChain Unit)** | `repo://libs/langchain_v1/tests/unit_tests/` | Unit tests for agent factory, middleware, chat models, and orchestration | +| **Tests (LangChain Integration)** | `repo://libs/langchain_v1/tests/integration_tests/` | Integration tests for agent execution, tool binding, and provider fallback | +| **Tests (Partner Unit)** | `repo://libs/partners/*/tests/unit_tests/` | Provider-specific unit tests | +| **Tests (Partner Integration)** | `repo://libs/partners/*/tests/integration_tests/` | Provider-specific integration tests | +| **Standard Tests** | `repo://libs/standard-tests/langchain_tests/` | Shared test suites and contracts for component conformance across the ecosystem | +| **Configuration (Core)** | `repo://libs/core/pyproject.toml` | langchain-core package metadata, dependencies (langsmith, httpx, tenacity, pydantic), and build config | +| **Configuration (LangChain)** | `repo://libs/langchain_v1/pyproject.toml` | langchain package metadata, core dependencies, and optional provider groups | +| **Configuration (Repo-Wide)** | `repo:///.pre-commit-config.yaml` | Git hooks for formatting, linting, and validation across all packages | +| **Build System (Libs)** | `repo://libs/Makefile` | Monorepo-level build targets, dependency locking, and cross-package tasks | + +## Key Directory Structure + +``` +/libs/ +├── core/ # langchain-core: Base abstractions (v1.6.1) +│ ├── langchain_core/ +│ │ ├── language_models/ # BaseChatModel and language model abstractions +│ │ ├── messages/ # Message types and content blocks +│ │ ├── runnables/ # Runnable protocol and composition +│ │ ├── tools/ # BaseTool and tool conversion +│ │ ├── prompts/ # Prompt templates and few-shot +│ │ ├── output_parsers/ # Output parsing and validation +│ │ ├── callbacks/ # Callback manager and handlers +│ │ ├── retrievers.py # Retriever abstraction +│ │ └── ... (other modules) +│ ├── tests/ +│ │ ├── unit_tests/ +│ │ └── integration_tests/ +│ ├── Makefile +│ └── pyproject.toml +│ +├── langchain_v1/ # langchain: Orchestration and agents (v1.4.0) +│ ├── langchain/ +│ │ ├── agents/ +│ │ │ ├── factory.py # Agent factory and graph construction +│ │ │ ├── middleware/ # Pluggable middleware system +│ │ │ ├── structured_output.py # Response schema and marshaling +│ │ │ └── _subagent_transformer.py # Sub-agent utilities +│ │ ├── chat_models/ +│ │ │ └── base.py # init_chat_model factory +│ │ ├── mcp/ # Model Context Protocol adapter +│ │ ├── messages/ # v1-specific message utilities +│ │ ├── embeddings/ # Embedding utilities +│ │ ├── tools/ # v1-specific tool utilities +│ │ └── rate_limiters/ # Rate limiting implementations +│ ├── tests/ +│ │ ├── unit_tests/ +│ │ ├── integration_tests/ +│ │ ├── benchmarks/ +│ │ └── cassettes/ # VCR cassettes for HTTP mocking +│ ├── Makefile +│ └── pyproject.toml +│ +├── partners/ # Provider-specific integrations +│ ├── openai/ # ChatOpenAI, embeddings, etc. +│ ├── anthropic/ # ChatAnthropic (Claude) +│ ├── ollama/ # ChatOllama (local models) +│ ├── groq/ # ChatGroq +│ ├── mistralai/ # ChatMistralAI +│ ├── huggingface/ # HuggingFace embeddings and models +│ ├── deepseek/ # ChatDeepSeek +│ ├── xai/ # XAI (Grok) +│ ├── perplexity/ # Perplexity models +│ ├── fireworks/ # Fireworks inference +│ ├── openrouter/ # OpenRouter aggregator +│ ├── chroma/ # Chroma vector store +│ ├── qdrant/ # Qdrant vector store +│ ├── exa/ # Exa search +│ ├── nomic/ # Nomic embeddings +│ └── Makefile +│ +├── model-profiles/ # LLM behavior and capability profiles +│ ├── langchain_model_profiles/ +│ ├── Makefile +│ └── pyproject.toml +│ +├── standard-tests/ # Cross-ecosystem test contracts +│ ├── langchain_tests/ +│ ├── tests/ +│ ├── Makefile +│ └── pyproject.toml +│ +├── text-splitters/ # Text splitting utilities +│ +├── Makefile # Multi-package build coordination +└── README.md +``` + +## Common Workflows + +### Finding Agent-Related Code +- **Agent construction**: `repo://libs/langchain_v1/langchain/agents/factory.py` +- **Middleware hooks**: `repo://libs/langchain_v1/langchain/agents/middleware/types.py` for type definitions; individual middleware in `repo://libs/langchain_v1/langchain/agents/middleware/` subdirectory +- **Structured output**: `repo://libs/langchain_v1/langchain/agents/structured_output.py` +- **Tests**: `repo://libs/langchain_v1/tests/unit_tests/` and `repo://libs/langchain_v1/tests/integration_tests/` + +### Finding LLM Integration Code +- **Provider implementations**: `repo://libs/partners//` (e.g., `repo://libs/partners/openai/`) +- **Model discovery**: `repo://libs/langchain_v1/langchain/chat_models/base.py` (init_chat_model) +- **Base interface**: `repo://libs/core/langchain_core/language_models/chat_models.py` +- **Model profiles**: `repo://libs/model-profiles/langchain_model_profiles/` + +### Finding Core Abstractions +- **Runnable protocol**: `repo://libs/core/langchain_core/runnables/base.py` +- **Messages**: `repo://libs/core/langchain_core/messages/` +- **Tools**: `repo://libs/core/langchain_core/tools/base.py` +- **Prompts**: `repo://libs/core/langchain_core/prompts/` +- **Callbacks**: `repo://libs/core/langchain_core/callbacks/` + +### Finding Tests +- **Core abstractions**: `repo://libs/core/tests/` +- **Agent and orchestration**: `repo://libs/langchain_v1/tests/` +- **Provider-specific**: `repo://libs/partners//tests/` +- **Shared test contracts**: `repo://libs/standard-tests/` + +### Configuration and Build +- **Lint and format**: `.pre-commit-config.yaml` at repo root +- **Core dependencies**: `repo://libs/core/pyproject.toml` +- **LangChain dependencies**: `repo://libs/langchain_v1/pyproject.toml` +- **Build tasks**: `repo://libs/Makefile` for multi-package commands + +## Build and Development Commands + +All package directories (`libs/core/`, `libs/langchain_v1/`, `libs/partners//`, etc.) include a local `Makefile` with standard targets: + +```bash +# Format code (ruff) +make -C format + +# Lint code (ruff) +make -C libs/core lint + +# Run all tests +make -C libs/langchain_v1 test + +# Lock dependencies +make -C libs/core lock + +# Check lockfile consistency +make -C libs/core check-lock +``` + +The root `repo://libs/Makefile` coordinates multi-package operations: + +```bash +# Lock all packages at once +make -C libs lock + +# Check all lockfiles +make -C libs check-lock +``` + +## Key Implementation Artifacts + +### Agent Factory Graph +The agent construction pipeline in `repo://libs/langchain_v1/langchain/agents/factory.py` builds a LangGraph state machine with these nodes: +- **Entry**: Runs `before_agent` middleware once +- **Loop Entry**: Begins each model iteration, runs `before_model` hooks +- **Model**: Invokes language model with message history +- **After Model**: Runs `after_model` hooks for response processing +- **Tools**: Executes tool calls (if any) +- **Exit**: Runs `after_agent` hooks once at completion + +Middleware can inject hooks at model boundaries, tool boundaries, and lifecycle hooks (`before_agent`, `before_model`, `after_model`, `after_tool_call`, `after_agent`). + +### Runnable Composition +The Runnable protocol in `repo://libs/core/langchain_core/runnables/base.py` enables declarative chaining via operators: +- **Piping** (`|`): Sequential composition +- **Parallel** (`+`): Parallel execution branches +- **Branching** (`.pipe()` with routing): Conditional execution paths +- **Fallback** (`.with_fallback()`): Error recovery with alternatives +- **Retry** (`.with_retry()`): Automatic retry with backoff + +All compositions automatically support `invoke()`, `ainvoke()`, `batch()`, `stream()`, and async variants. + +### Message Protocol +The message abstraction in `repo://libs/core/langchain_core/messages/` defines: +- **Message types**: `AIMessage`, `HumanMessage`, `SystemMessage`, `ToolMessage`, `FunctionMessage` +- **Content blocks**: `TextBlock`, `ImageBlock`, `ToolUseBlock`, `ToolResultBlock`, custom blocks +- **Message utilities**: Serialization, merging, role mapping, model-specific translation + +### Tool Abstraction +The `BaseTool` in `repo://libs/core/langchain_core/tools/base.py` provides: +- **Tool protocol**: Sync/async invoke, schema generation from docstrings/Pydantic +- **Conversion**: Helper functions to wrap Python functions as tools +- **Rendering**: Format tools for model context as descriptions or structured schemas + +## Dependency Flow + +``` +User Applications + ├─→ langchain (v1.4.0) + │ ├─→ langchain-core (v1.6.1) + │ └─→ LangGraph (state machines) + │ + ├─→ langchain-core (direct use) + │ + └─→ Partner Packages (langchain-openai, langchain-anthropic, etc.) + └─→ Implement langchain-core abstractions +``` + +**Versioning**: Core is released independently with strict semantic versioning. LangChain and partners pin core versions. Partner packages are released independently per provider. diff --git a/openwiki/streaming.md b/openwiki/streaming.md new file mode 100644 index 0000000000..0c09a7dfb3 --- /dev/null +++ b/openwiki/streaming.md @@ -0,0 +1,346 @@ +--- +type: "Concept" +title: "Streaming: Token-by-Token Output" +description: "How streaming works across LLM components and chains, token-by-token delivery via AIMessageChunk, callback integration, and memory/latency tradeoffs." +tags: [streaming, token-streaming, llm-output, chat-models, callbacks, astream, real-time-feedback] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-c9313cf42f0120d86b20245f + resource: repo://libs/core/langchain_core/callbacks/base.py + - id: openwiki-source-c7a2c3ef4ec61c3e28011205 + resource: repo://libs/core/langchain_core/callbacks/streaming_stdout.py + - id: openwiki-source-5f8bc32563177d89fbab9b2f + resource: repo://libs/core/langchain_core/language_models/chat_model_stream.py + - id: openwiki-source-c52037e7b642f7ac5a7642a8 + resource: repo://libs/core/langchain_core/language_models/chat_models.py + - id: openwiki-source-77dc1fb726463969f9d53658 + resource: repo://libs/core/langchain_core/messages/ai.py + - id: openwiki-source-a1981e868973f6fd7f71e12e + resource: repo://libs/core/langchain_core/runnables/base.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +**Streaming** is the mechanism by which LangChain delivers model output incrementally, token by token, rather than waiting for the entire response. This enables real-time feedback in web UIs, console displays, and other user-facing contexts, and forms the foundation for building responsive applications that do not block on model latency. + +Instead of blocking with `invoke()` until a full response is ready, applications call `stream()` or `astream()` and receive a sequence of partial outputs as they arrive from the model. Each chunk is an `AIMessageChunk` carrying delta content. Callbacks intercept these chunks via the `on_llm_new_token` event, making it possible to observe, log, or react to each token without collecting the entire response first. + +Streaming flows through chains—prompts, models, output parsers, and other runnables—preserving incremental output delivery at each stage. By composition, a chain automatically supports streaming if all its components do. This page documents the mechanics of streaming across components, the trade-offs versus non-streaming invoke, and how to integrate streaming into applications. + +## Synchronous Streaming: stream() + +**Location**: `repo://libs/core/langchain_core/language_models/chat_models.py#L727-L856` + +`BaseChatModel.stream()` is the primary synchronous streaming entry point. It yields `AIMessageChunk` objects as they are produced by the underlying model, with incremental content—a single token, a fragment of JSON, or a structured block update. + +### Control Flow + +1. **Check if streaming is implemented**: `_should_stream()` determines whether the model supports streaming. If not, `stream()` falls back to `invoke()` and yields one complete result. + +2. **Initialize callbacks**: A `CallbackManager` is configured from the provided `RunnableConfig`, binding callbacks, tags, and metadata. + +3. **Fire on_chat_model_start**: The callback lifecycle begins with `on_chat_model_start`, signaling that LLM invocation is beginning. + +4. **Iterate model chunks**: For each `ChatGenerationChunk` from the underlying `_stream()` implementation: + - The chunk's message ID is set to a unique run ID if not already present. + - Response metadata (model provider, latency, etc.) is computed and attached. + - **on_llm_new_token is fired** with the chunk's content and the full chunk object, allowing callbacks to observe or buffer each token. + - The chunk message is cast to `AIMessageChunk` and yielded immediately. + - Chunks are accumulated for later aggregation. + +5. **Yield final "last" chunk**: After the model finishes, if output_version is v1 (content-block format), an empty chunk with `chunk_position="last"` is yielded. This signals to parsers and consumers that the stream is complete and that tool_call_chunks should be finalized. + +6. **Callback lifecycle closes**: If successful, `on_llm_end` fires with a merged `ChatGeneration` containing all chunks. If an exception occurs, `on_llm_error` fires with partial accumulation. + +### Fallback Behavior + +If the model does not implement streaming (checked via `_should_stream(async_api=False)`), `stream()` delegates to `invoke()` and yields a single result cast to `AIMessageChunk`. This ensures all models provide a consistent streaming interface, even if only non-streaming invoke is available. + +### Rate Limiting + +If a rate limiter is attached to the model, `stream()` acquires a permit before beginning, blocking until the rate limit allows. + +## Asynchronous Streaming: astream() + +**Location**: `repo://libs/core/langchain_core/language_models/chat_models.py#L858-L991` + +`BaseChatModel.astream()` is the async variant of `stream()`, mirroring the synchronous logic but using async/await and `AsyncCallbackManager`. + +**Key differences**: +- Uses `await` for callback events (`await run_manager.on_llm_new_token(...)`, `await run_manager.on_llm_end(...)`) +- Iterates via `async for chunk in self._astream(...)` +- Acquires rate limit via `await self.rate_limiter.aacquire(blocking=True)` + +The async streaming protocol is identical to sync: yield chunks immediately, fire callbacks per token, finalize tool call chunks on the "last" signal. + +## AIMessageChunk: Incremental Content + +**Location**: `repo://libs/core/langchain_core/messages/ai.py#L418-L536` + +`AIMessageChunk` is the message type yielded during streaming. Unlike `AIMessage`, it represents a **partial, incremental update** to a conversation message and supports merging via the `+` operator. + +### Structure + +- **content**: String or list of content blocks. During streaming, each chunk contains only the new token(s) or delta for that step. +- **tool_call_chunks**: List of `ToolCallChunk` objects (incomplete tool calls being streamed). These are progressively updated as arguments arrive. +- **chunk_position**: Optional sentinel; when set to `"last"`, indicates the final chunk in the stream, triggering finalization of tool calls and reasoning blocks. +- **response_metadata**: Model-specific metadata (latency, model_provider, usage counters, etc.) attached by the streaming handler. + +### Merging and Aggregation + +Streaming chunks accumulate via the `+` operator, which merges content, concatenates tool_call arguments, and combines metadata. A complete `AIMessage` with finalized `tool_calls` (not chunks) is reconstructed when chunks are merged or when the "last" signal is received. + +## Callback Integration: on_llm_new_token + +**Location**: `repo://libs/core/langchain_core/callbacks/base.py#L65-L88` + +The `on_llm_new_token` callback fires for each token or chunk during streaming, enabling real-time observation and logging. + +### Signature + +```python +def on_llm_new_token( + self, + token: str | list[str | dict[str, Any]], + *, + chunk: GenerationChunk | ChatGenerationChunk | None = None, + run_id: UUID, + parent_run_id: UUID | None = None, + tags: list[str] | None = None, + **kwargs: Any, +) -> Any: +``` + +- **token**: The string token or list of content blocks (when output_version="v1"). +- **chunk**: The full `ChatGenerationChunk` carrying metadata, message ID, response metadata, and tool_call_chunks. +- **run_id**: Unique identifier for this streaming run, used for tracing and correlation. + +### Example: Stream to stdout + +```python +from langchain_core.callbacks import StreamingStdOutCallbackHandler + +callback = StreamingStdOutCallbackHandler() + +# Callbacks are passed via RunnableConfig +for chunk in model.stream( + messages, + config=RunnableConfig(callbacks=[callback]) +): + pass # callback prints each token to stdout +``` + +The `StreamingStdOutCallbackHandler` implements `on_llm_new_token` to write tokens to `sys.stdout`, making streaming output visible in real-time. + +## Streaming Through Chains + +Streaming flows through chains composed of runnables (prompts, models, parsers). The streaming protocol is implemented at each stage via the `stream()` and `transform()` methods on `Runnable`. + +### Default Behavior + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L1194-L1235` + +By default, `Runnable.stream()` yields one full output from `invoke()`. Subclasses that support streaming override `stream()` or `transform()` to yield chunks. + +### Streaming through RunnableSequence + +**Location**: `repo://libs/core/langchain_core/runnables/base.py#L3075-L3320` + +`RunnableSequence` (a chain created with the `|` operator) automatically supports streaming if: +1. **All upstream components implement transform**: The `transform()` method maps streaming input to streaming output. +2. **The last component produces chunks**: Output parsers and models implement `transform()` to yield partial results. + +If any component does not implement `transform()`, streaming begins only after that component completes (blocking point). Multiple blocking components create multiple buffering points, but the final output still streams from the last component if it supports streaming. + +### Streaming Example: Model → Parser + +```python +from langchain_openai import ChatOpenAI +from langchain_core.output_parsers import StrOutputParser + +model = ChatOpenAI() +parser = StrOutputParser() +chain = model | parser + +# stream yields parser outputs incrementally as tokens arrive +for chunk in chain.stream("What is 2+2?"): + print(chunk, end="", flush=True) +``` + +When `model.stream()` yields chunks, the parser's `transform()` (or default `stream()`) consumes each chunk and yields its transformation. Text parsers may yield tokens directly; JSON parsers yield partial JSON objects as they become parseable. + +## Streaming via stream_events: ChatModelStream + +**Location**: `repo://libs/core/langchain_core/language_models/chat_model_stream.py` + +For advanced use cases requiring detailed event granularity, `BaseChatModel.stream_events(version="v3")` returns a `ChatModelStream` object that exposes **typed projection properties** (`.text`, `.tool_calls`, `.usage`, `.reasoning`, `.output`) which accumulate events as they arrive. + +This is distinct from simple token streaming and is useful for applications needing structured, event-by-event visibility into reasoning, tool calls, and other protocol events. The `ChatModelStream` also fires `on_stream_event` callbacks for each protocol event, not just tokens. + +## Memory and Latency Trade-offs: stream() vs invoke() + +### invoke() + +- **Latency**: Waits for the entire model response before returning. +- **Memory**: No intermediate storage required; only the final message is held. +- **Responsiveness**: Blocks the calling thread/coroutine until complete. +- **Use case**: Batch processing, when a complete response is needed upfront. + +### stream() + +- **Latency**: Yields the first token as soon as available; responsive to user. +- **Memory**: Requires buffering of accumulated chunks if the caller collects them. +- **Responsiveness**: Non-blocking; enables progressive display. +- **Use case**: Web UIs, console applications, user-facing interactions where real-time feedback improves UX. + +In practice, streaming does not add significant latency compared to invoke; the model produces tokens at the same rate. The difference is **when tokens are delivered to the caller**. Stream delivery is preferable for interactive applications because users see output appearing in real-time rather than a blank screen until the full response is ready. + +## Integration Patterns + +### Real-time Console Output + +```python +from langchain_core.callbacks import StreamingStdOutCallbackHandler + +callback = StreamingStdOutCallbackHandler() +for _ in model.stream( + messages, + config=RunnableConfig(callbacks=[callback]) +): + pass # Tokens are printed as they arrive +``` + +### Accumulate Streamed Output + +```python +result = "" +for chunk in model.stream(messages): + result += chunk.content or "" +print(result) # Final complete response +``` + +### Custom Callback for Application Logic + +```python +from langchain_core.callbacks import BaseCallbackHandler + +class MyCallback(BaseCallbackHandler): + def on_llm_new_token(self, token, **kwargs): + # React to each token (e.g., update UI, log, rate-limit) + self.buffer.append(token) + +for _ in model.stream( + messages, + config=RunnableConfig(callbacks=[MyCallback()]) +): + pass +``` + +### Async Streaming in Web Framework + +```python +async def chat_endpoint(messages): + async for chunk in model.astream(messages): + # Yield to HTTP client as server-sent event + yield f"data: {chunk.content}\n\n" +``` + +## Lifecycle and Error Handling + +### Successful Stream + +1. `on_chat_model_start` fires +2. For each chunk: `on_llm_new_token` fires +3. `on_llm_end` fires with merged `ChatGeneration` + +### Stream with Error + +1. `on_chat_model_start` fires +2. For each chunk before error: `on_llm_new_token` fires +3. Error occurs in `_stream()` or callback +4. `on_llm_error` fires with partial chunks aggregated +5. Exception is re-raised to caller + +### Cleanup + +When a stream exits (via break, exception, or normal completion), any buffered chunks are merged and callbacks finalize the run. Async streaming also closes async generators via `aclose()` if present. + +## Extension Points + +### Custom Streaming Implementation + +Subclasses of `BaseChatModel` override `_stream()` and/or `_astream()` to implement model-specific streaming: + +```python +class MyModel(BaseChatModel): + def _stream( + self, + messages: list[BaseMessage], + stop: list[str] | None = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + # Yield ChatGenerationChunk for each token + for token in model_api.stream(messages, stop=stop, **kwargs): + yield ChatGenerationChunk(message=AIMessageChunk(content=token)) +``` + +The `stream()` method handles callbacks, merging, and lifecycle; subclasses only implement the core streaming loop. + +### Custom Output Parser Transform + +Output parsers can override `transform()` to stream partial results: + +```python +class MyParser(BaseGenerationOutputParser[T]): + def transform( + self, + input: Iterator[str | BaseMessage], + config: RunnableConfig | None = None, + **kwargs: Any, + ) -> Iterator[T]: + buffer = "" + for chunk in input: + buffer += chunk.content or "" + # Attempt partial parsing + if partial := self.parse_result([Generation(text=buffer)], partial=True): + yield partial +``` + +This allows parsers to yield progressively more complete results as tokens arrive. + +## Configuration and Operations + +### Disabling Streaming + +Models respect the `stream=False` parameter or a falsy check in `_should_stream()`. Calling `invoke()` directly bypasses streaming even if the model supports it. + +### Configuring Callbacks + +```python +config = RunnableConfig( + callbacks=[StreamingStdOutCallbackHandler()], + tags=["user-interaction"], + metadata={"session_id": "..."}, +) +for chunk in model.stream(messages, config=config): + pass +``` + +Callbacks, tags, and metadata propagate through the callback lifecycle. + +### Async Streaming + +Use `astream()` in async contexts and `await` on async callbacks: + +```python +async for chunk in model.astream(messages): + # Process chunks asynchronously + await handle_chunk(chunk) +``` + +## Conclusion + +Streaming is central to building responsive LangChain applications. By yielding output token-by-token and firing callbacks per token, streaming enables real-time user feedback without sacrificing performance. The protocol is consistent across models, chains, and parsers, making it easy to compose streaming operations and observe output at any level of the application stack. diff --git a/openwiki/structured-output.md b/openwiki/structured-output.md new file mode 100644 index 0000000000..5638acd933 --- /dev/null +++ b/openwiki/structured-output.md @@ -0,0 +1,428 @@ +--- +type: "Reference" +title: "AutoStrategy (recommended)" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-71e882e1ac9757ea8e959a7c + resource: repo://libs/langchain_v1/langchain/agents/factory.py + - id: openwiki-source-ec30ab6256dd50cc670919f6 + resource: repo://libs/langchain_v1/langchain/agents/structured_output.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +Structured output is the mechanism that ensures a language model returns responses matching a specific JSON schema. Rather than receiving unparsed text or tool calls, agents can enforce that model outputs conform to Pydantic models, dataclasses, TypedDicts, or raw JSON schemas. The factory configures one of three strategies—tool-based, provider-native, or automatic—each with different tradeoffs around compatibility, validation, and retry behavior. + +### Core Concept + +When an agent is created with a `response_format` parameter, it tells the model "all your responses must match this schema." The agent: + +1. **Registers** the schema as an artificial tool (for tool-calling strategy) or sends it to the model provider's native API (for provider-native strategy) +2. **Detects** whether the model supports native structured output (AutoStrategy) +3. **Parses** the model's response against the schema using Pydantic's `TypeAdapter` for validation +4. **Retries** automatically on validation errors (if enabled via `handle_errors`) +5. **Stores** the parsed result in `structured_response` state field + +This is distinct from tool calling—structured output constrains the response itself, not the model's tool invocations. + +## Response Format Strategies + +Three strategies control how structured output is enforced: + +### ToolStrategy: Tool-Based Structured Output + +The model is presented with an artificial tool whose name and arguments match the schema. When the model calls this tool, its arguments are parsed and validated against the schema. + +**Lifecycle:** + +1. Schema is wrapped as a `StructuredTool` with the schema's JSON schema as `args_schema` +2. Tool is added to the model's tool list with `tool_choice="any"` to force use +3. Model generates a tool call with the schema name +4. Tool call arguments are parsed via `_parse_with_schema` using Pydantic's `TypeAdapter` +5. Parsed result stored in `structured_response` +6. Empty `ToolMessage` returned (tool has no real execution) + +**Advantages:** + +- Works with all models that support tool calling +- Full validation available for non-dict schemas +- Automatic retry on validation errors (configurable) +- Supports Union types (multiple schema variants) + +**Limitations:** + +- Requires tool calling capability +- Adds tool to the tool list (may consume tool slot on some models) +- Raw JSON schema dicts skip validation, making `handle_errors` inert + +**Error Handling:** + +The `handle_errors` parameter controls retry behavior on validation failure: + +- `True` (default): Catch all errors, retry with default error template +- `False`: Let exceptions propagate without retry +- `str`: Catch all errors, retry with custom message +- `type[Exception]` or `tuple[type[Exception], ...]`: Only retry specific exception types +- `Callable[[Exception], str]`: Custom function returns retry message per exception + +Failed parses generate a `ToolMessage` with the error message, allowing the model to correct its output. + +### ProviderStrategy: Native Structured Output + +The schema is sent to the model provider's native structured output API (e.g., OpenAI's `response_format` with `type: "json_schema"`). The provider enforces schema compliance on their side; the agent only needs to parse the JSON response. + +**Lifecycle:** + +1. Schema converted to JSON Schema via Pydantic's `model_json_schema()` +2. Wrapped in provider-specific format: `{"type": "json_schema", "json_schema": {"name": ..., "schema": ..., "strict": ...}}` +3. Passed to model via `model.bind(..., response_format={...})` +4. Model returns JSON text (guaranteed valid by provider) +5. Text parsed via `json.loads()` then validated against schema +6. Parsed result stored in `structured_response` + +**Advantages:** + +- Provider enforces schema—no invalid JSON possible +- Doesn't consume tool slots +- Works alongside tool calling +- Strict mode available (supported providers only) + +**Limitations:** + +- Limited to models with native structured output (OpenAI, Claude, etc.) +- No automatic retry on validation errors (provider side is strict) +- Must explicitly test model capability support + +**Capability Detection:** + +A model supports provider-native structured output if: + +1. Its profile includes `"structured_output": True` (checked via `model.profile`), AND +2. Not a pre-3-series Gemini model (which cannot mix tools with structured output), OR +3. Model name matches fallback patterns like `gpt-4o`, `claude-opus`, etc. + +### AutoStrategy: Automatic Strategy Selection + +Defers strategy selection until model invocation time. The factory inspects the bound model and chooses: + +1. **ProviderStrategy** if the model supports native structured output +2. **ToolStrategy** as fallback for all other models + +**Lifecycle:** + +1. User passes raw schema or `AutoStrategy(schema=...)` +2. Factory converts to `ToolStrategy` upfront to pre-build tools +3. At model call time, `_supports_provider_strategy()` checks model capabilities +4. If supported, `ProviderStrategy` is created and model kwargs bound +5. Otherwise, `ToolStrategy` is used (tools already prepared) + +**Advantage:** Best of both worlds—uses provider when available, falls back to tools. + +```python +from langchain.agents import create_agent +from pydantic import BaseModel + +class WeatherResponse(BaseModel): + """Weather forecast response.""" + location: str + temperature: int + condition: str + +# AutoStrategy (recommended) +agent = create_agent( + model="openai:gpt-4o", + response_format=WeatherResponse, # Wrapped in AutoStrategy automatically +) + +# Explicit strategies +from langchain.agents.structured_output import ProviderStrategy, ToolStrategy + +agent_native = create_agent( + model="openai:gpt-4o", + response_format=ProviderStrategy(schema=WeatherResponse), +) + +agent_tools = create_agent( + model="openai:gpt-4o", + response_format=ToolStrategy( + schema=WeatherResponse, + handle_errors=True, # Retry on validation failure + ), +) +``` + +## Schema Types + +Supported schema types for structured output: + +| Type | Example | Validation | Tool Use | +|------|---------|-----------|----------| +| **Pydantic model** | `class Response(BaseModel): ...` | Full validation | Yes (via TypeAdapter) | +| **Dataclass** | `@dataclass class Response: ...` | Full validation | Yes (via TypeAdapter) | +| **TypedDict** | `class Response(TypedDict): ...` | Full validation | Yes (via TypeAdapter) | +| **JSON Schema dict** | `{"type": "object", "properties": {...}}` | None (dict schemas skip validation) | Returns dict as-is | + +The factory normalizes all types via `_SchemaSpec`, which: + +- Extracts schema name (class name, `title` field, or generated UUID fragment) +- Extracts description (docstring, `description` field, or empty) +- Computes JSON Schema representation for tool binding +- Tracks `strict` mode flag for provider-side enforcement + +## Integration with Agent Factory + +The `create_agent()` function integrates structured output through: + +### 1. Upfront Schema Registration + +```python +# At agent creation time +if tool_strategy_for_setup: + for response_schema in tool_strategy_for_setup.schema_specs: + structured_tool_info = OutputToolBinding.from_schema_spec(response_schema) + structured_output_tools[structured_tool_info.tool.name] = structured_tool_info +``` + +Pre-builds `OutputToolBinding` instances wrapping schemas as `StructuredTool` instances. These bindings store the original schema, its classification (`pydantic`, `dataclass`, etc.), and the tool for later parsing. + +### 2. Model Binding During Invocation + +The `_get_bound_model()` function (called on each model invocation) performs auto-detection: + +```python +# Determine effective response format (auto-detect if needed) +effective_response_format: ResponseFormat[Any] | None +if isinstance(response_format, AutoStrategy): + if _supports_provider_strategy(request.model, tools=request.tools): + effective_response_format = ProviderStrategy(schema=response_format.schema) + else: + effective_response_format = ToolStrategy(schema=response_format.schema) +else: + effective_response_format = response_format +``` + +Then binds the model: + +- **ProviderStrategy**: `model.bind(..., response_format={...})` +- **ToolStrategy**: `model.bind_tools(final_tools, tool_choice="any", ...)` + +### 3. Response Parsing + +After model invocation, `_handle_model_output()` dispatches to the appropriate parser: + +**For ProviderStrategy:** + +```python +if isinstance(effective_response_format, ProviderStrategy): + if not output.tool_calls: + provider_strategy_binding = ProviderStrategyBinding.from_schema_spec(...) + structured_response = provider_strategy_binding.parse(output) + return {"messages": [output], "structured_response": structured_response} +``` + +**For ToolStrategy:** + +```python +if isinstance(effective_response_format, ToolStrategy): + structured_tool_calls = [tc for tc in output.tool_calls if tc["name"] in structured_output_tools] + if structured_tool_calls: + # Single call: parse args, handle errors, return response + structured_response = structured_output_tools[tool_call["name"]].parse(tool_call["args"]) + return {"messages": [...], "structured_response": structured_response} +``` + +## OutputToolBinding: Schema to Tool Conversion + +`OutputToolBinding` is the bridge between a schema and a tool. It stores: + +- **schema**: Original schema (Pydantic, dataclass, TypedDict, or dict) +- **schema_kind**: Classification (`'pydantic'`, `'dataclass'`, `'typeddict'`, `'json_schema'`) +- **tool**: `StructuredTool` instance with `args_schema` bound to the JSON schema + +The `parse()` method reconstructs the original type from tool call arguments: + +```python +def parse(self, tool_args: dict[str, Any]) -> SchemaT | dict[str, Any]: + return _parse_with_schema(self.schema, self.schema_kind, tool_args) +``` + +**Parsing Flow:** + +1. For dict schemas: Return arguments as-is (no validation) +2. For typed schemas: Use Pydantic's `TypeAdapter` to validate Python type +3. On validation error: Raise `ValueError` with schema name and error details + +This allows the factory to maintain a single mapping of structured output tool names to their binding metadata throughout the agent's lifetime, enabling quick lookup during response handling. + +## Error Handling and Validation + +### Error Types + +**StructuredOutputError** (base class): + +- Holds the `AIMessage` that caused the error +- Parent of specific error types + +**MultipleStructuredOutputsError**: + +Raised when a single structured output schema is expected but the model calls multiple structured output tools. + +```python +tool_names = [tc["name"] for tc in structured_tool_calls] +exception = MultipleStructuredOutputsError(tool_names, output) +``` + +**StructuredOutputValidationError**: + +Raised when tool call arguments fail to parse according to the schema. + +```python +exception = StructuredOutputValidationError(tool_name, source_exception, output) +``` + +### Retry Logic (ToolStrategy Only) + +When `handle_errors` is enabled and a validation error occurs during tool parsing: + +1. `_handle_structured_output_error()` determines if retry should happen +2. Returns `(should_retry: bool, error_message: str)` +3. If retry: Error message wrapped in `ToolMessage` appended to conversation +4. Model receives error context and can correct its response + +**Error Callback:** + +```python +should_retry, error_message = _handle_structured_output_error( + exception, effective_response_format +) +if not should_retry: + raise exception from exc + +# Return error message to model +return { + "messages": [ + output, + ToolMessage( + content=error_message, + tool_call_id=tool_call["id"], + name=tool_call["name"], + ), + ], +} +``` + +The model's next turn receives the error and can attempt to correct the output. + +### Validation Limitations + +**Dict schemas skip validation:** Raw JSON schema dicts (not Pydantic, dataclass, or TypedDict) return arguments as-is: + +```python +if schema_kind == "json_schema": + return data # No validation, no retry possible +``` + +To enable validation and retries, express schemas as Pydantic models or TypedDicts. + +**Provider strategy has no retry:** Native structured output is provider-enforced; the agent receives valid JSON or an API error. No in-conversation retry is possible for schema mismatches. + +## State and Lifecycle + +### State Fields + +The agent state includes structured output handling via: + +- **messages**: Includes tool calls and tool messages from structured output invocation +- **structured_response**: Holds the parsed schema instance (set when output is valid, cleared on error retry) + +### Lifecycle Events + +``` +User input + ↓ +[pre-model middleware] + ↓ +_get_bound_model() → detect strategy, bind model with tools or provider format + ↓ +model.invoke() → model returns AIMessage with tool_calls (ToolStrategy) + or text (ProviderStrategy) + ↓ +_handle_model_output() → parse response, validate against schema + ↓ +[structured_response set] or [error + retry message] + ↓ +[post-model middleware] + ↓ +Return to user or continue loop +``` + +## Configuration and Middleware + +Middleware can override `response_format` at invocation time via `ModelRequest.override()`: + +```python +class MyMiddleware(AgentMiddleware): + def wrap_model_call(self, request, handler): + # Narrow union response format to a specific variant + narrow_format = ToolStrategy(schema=request.response_format.schema_specs[0]) + return handler(request.override(response_format=narrow_format)) +``` + +The agent re-detects strategy and rebuilds tool bindings on each invocation, allowing dynamic schema changes. However, **all structured output schemas must be declared upfront**—middleware cannot add new schemas not present in the initial `response_format`. + +## Example: Weather Agent with Structured Output + +```python +from pydantic import BaseModel +from langchain.agents import create_agent +from langchain_openai import ChatOpenAI + +class WeatherResponse(BaseModel): + """Current weather forecast.""" + location: str + temperature_f: int + condition: str + humidity_percent: int + +# Create agent with structured output +agent = create_agent( + model=ChatOpenAI(model="gpt-4o"), + response_format=WeatherResponse, # AutoStrategy + system_prompt="You are a weather forecaster. Return accurate weather data.", +) + +# Invoke +result = agent.invoke({ + "messages": [{"role": "user", "content": "What's the weather in San Francisco?"}] +}) + +# Access structured response +weather: WeatherResponse = result["structured_response"] +print(f"Temperature: {weather.temperature_f}°F, Condition: {weather.condition}") +``` + +With explicit error handling: + +```python +from langchain.agents.structured_output import ToolStrategy + +agent = create_agent( + model="openai:gpt-4o", + response_format=ToolStrategy( + schema=WeatherResponse, + handle_errors=True, # Retry on validation errors + tool_message_content="Invalid weather data format. Please provide: location, temperature_f, condition, humidity_percent.", + ), +) +``` + +## See Also + +- [Agent Factory](/openwiki/agent-factory.md): Entry point for creating agents; handles schema registration and strategy binding +- [Agent Execution Flow](/openwiki/agent-execution.md): Runtime loop where structured output is parsed and validated +- [LangChain Structured Output Documentation](https://python.langchain.com/docs/guides/structured_output/) diff --git a/openwiki/tools.md b/openwiki/tools.md new file mode 100644 index 0000000000..323cf372c3 --- /dev/null +++ b/openwiki/tools.md @@ -0,0 +1,510 @@ +--- +type: "Reference" +title: "Form 1: No arguments (name from function)" +openwiki_generated: true +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-9861ba5cf0c42c142cf732f9 + resource: repo://libs/core/langchain_core/messages/tool.py + - id: openwiki-source-4ff475d7b00540f962384251 + resource: repo://libs/core/langchain_core/tools/base.py + - id: openwiki-source-9c422fcb5ac12738f17d1cd1 + resource: repo://libs/core/langchain_core/tools/convert.py + - id: openwiki-source-1ab4436ccb637ddf41e35732 + resource: repo://libs/core/langchain_core/tools/render.py + - id: openwiki-source-80e84f93417c922f44011393 + resource: repo://libs/core/langchain_core/tools/simple.py + - id: openwiki-source-b816e651a5890bde13cf8013 + resource: repo://libs/core/langchain_core/tools/structured.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + + +## Overview + +LangChain's tool system enables agents and language models to execute structured actions by converting Python functions and Runnables into schema-aware components. Tools form the core execution mechanism for agentic workflows, providing automatic argument validation, error handling, and integration with callback systems. + +The tool ecosystem consists of three layers: + +1. **BaseTool**: Core abstract interface defining tool protocol and execution semantics +2. **Tool Types**: Concrete implementations (StructuredTool, Tool) for different input patterns +3. **Tool Creation**: Decorators and factories (@tool, convert_runnable_to_tool) that generate tools from functions and runnables + +## BaseTool Protocol and Core Responsibilities + +BaseTool is the abstract base class extending RunnableSerializable that defines the contract for all tools. Every tool carries three essential descriptors and configuration for execution control. + +**Required Properties:** +- `name: str` — Unique identifier that clearly communicates purpose; used by agents and models to select tools +- `description: str` — Human-readable text explaining when and why to use the tool; guides model decisions +- `args_schema: TypeBaseModel | dict | None` — Pydantic model or JSON schema dict specifying valid input arguments + +**Execution Control:** +- `return_direct: bool` — When True, agent stops looping immediately after tool execution (terminal action) +- `response_format: "content" | "content_and_artifact"` — If "content_and_artifact", tool must return a two-tuple (content, artifact) for structured output with optional artifacts +- `handle_tool_error: bool | str | Callable` — Strategy for ToolException: False (re-raise), True (use exception message), str (fixed message), or callable (custom handler) +- `handle_validation_error: bool | str | Callable` — Strategy for pydantic ValidationError during input parsing + +**Callbacks & Metadata:** +- `callbacks: Callbacks` — Lifecycle callbacks (on_tool_start, on_tool_end, on_tool_error) for tracing and monitoring +- `tags: list[str]` — Optional semantic labels attached to all invocations for filtering and metrics +- `metadata: dict` — Custom application-specific metadata passed to callbacks +- `verbose: bool` — Whether to log tool progress + +**Provider Integration:** +- `extras: dict[str, Any]` — Provider-specific configuration (e.g., Anthropic cache_control, defer_loading) passed to chat models during tool rendering + +## Input Schema Generation and Validation + +Tool input validation is built on Pydantic models generated from function signatures. The schema generation pipeline handles both automatic inference and explicit specification. + +**Schema Sources (by precedence):** +1. Explicit `args_schema` parameter provided to tool decorator or factory +2. JSON schema dict if `args_schema` is already a dict +3. Inferred from function signature via `create_schema_from_function()` + +**Inference Process:** + +When `infer_schema=True` (default), the tool examines the function signature to generate a Pydantic model: + +- Type hints are extracted via `get_type_hints()` with support for `Annotated` types +- Function docstring is parsed (if `parse_docstring=True`) following Google style to extract parameter descriptions +- Descriptions are merged from: Annotated metadata → docstring Args section → none +- Injected arguments (those annotated with `InjectedToolArg`, `InjectedToolCallId`, or `ToolRuntime`) are automatically excluded from the schema sent to models but re-injected at runtime +- Reserved parameter names (`run_manager`, `callbacks`, `config`) are filtered from the user-facing schema + +**Memoization:** The `tool_call_schema` property builds and caches a subset model class per tool instance, excluding injected arguments. The schema class's `model_json_schema()` method is patched to cache the generated dict, preventing expensive regeneration on every agent loop. + +**Input Parsing and Validation:** + +During execution, tool input is parsed by `_parse_input()`: +- String input is mapped to the single argument if the schema defines exactly one field +- Dict input is validated via Pydantic, with Annotated descriptions providing field documentation +- Injected arguments are identified by signature inspection and re-injected from tool metadata or invocation context (e.g., `tool_call_id`, `ToolRuntime`) +- Validation errors are caught and handled according to `handle_validation_error` configuration + +**Annotation-Driven Descriptions:** + +Parameter descriptions can come from Annotated field metadata: + +```python +from typing import Annotated +from pydantic import Field +from langchain_core.tools import tool + +@tool +def my_function( + query: Annotated[str, Field(description="The search query")], + limit: Annotated[int, "Maximum number of results"] = 10 +) -> str: + return f"search: {query}" +``` + +Both `Field(description=...)` and direct string annotations are supported and merged into the generated schema. + +## ToolCall and ToolMessage: Request-Response Protocol + +Tools are invoked via ToolCall objects and respond with ToolMessage objects, enabling structured communication in agentic loops. + +**ToolCall (from messages/tool.py):** + +A ToolCall is a TypedDict representing a model's request to execute a tool: + +```python +{ + "name": "search_tool", # Tool name to invoke + "args": {"query": "python"}, # Validated arguments as dict + "id": "call_abc123", # Unique ID for pairing with response + "type": "tool_call" # Discriminator +} +``` + +Multiple ToolCalls can be streamed and merged via `AIMessageChunk`, with streaming yielding `ToolCallChunk` objects that progressively build the arguments JSON string. + +**ToolMessage (from messages/tool.py):** + +Returned by tools to communicate results back to the model: + +```python +ToolMessage( + content="Result of the tool execution", + tool_call_id="call_abc123", # Must match ToolCall.id + name="search_tool", # Tool name (optional) + artifact={"raw": "data"}, # Unshown to model (optional) + status="success" # "success" or "error" +) +``` + +- `artifact`: Stores full tool output when only a summary is sent to the model +- `status`: Allows tools to report errors without raising exceptions (e.g., when `handle_tool_error=True`) +- Content supports rich formatting: plain text or list of message content blocks (images, JSON, search results, documents, etc.) + +**ToolOutputMixin:** An empty mixin class used to identify custom objects that tools can return directly without coercion to string. Tools can return ToolOutputMixin instances or lists of them, bypassing automatic ToolMessage wrapping. + +## Execution: run() and arun() Methods + +Both synchronous and asynchronous execution follow the same lifecycle: + +1. **Configuration:** Merge callbacks from tool config, invocation args, and runnable config +2. **Parsing:** Convert tool input (str/dict/ToolCall) to function args/kwargs via `_parse_input()` and `_to_args_and_kwargs()` +3. **Injection:** Inject runtime values (run_manager, callbacks, RunnableConfig) if function signature declares them +4. **Execution:** Call `_run()` or `_arun()` within callback context, propagating config through context variables +5. **Formatting:** Convert output to ToolMessage if invoked with `tool_call_id`, preserving status and artifacts +6. **Error Handling:** Catch ToolException and ValidationError, apply handler strategy (re-raise, return message, or invoke custom handler) + +**Callback Lifecycle:** + +- `on_tool_start()`: Fired before execution with filtered inputs (injected args removed), tool metadata, and trace ID +- `on_tool_end()`: Fired after success with formatted output +- `on_tool_error()`: Fired on exception with the exception and trace ID + +**Config Propagation:** + +RunnableConfig passed to invoke/ainvoke is patched with child callbacks and injected into tool execution context, enabling nested tools and state/store access via `ToolRuntime` parameters. + +## Tool Types: Tool and StructuredTool + +LangChain provides two concrete tool implementations with different input handling semantics. + +**Tool (simple.py):** +- Single-input tool expecting string or dict coercion to string +- No explicit args schema required; defaults to `{"tool_input": {"type": "string"}}` +- Used for simple function wrappers and legacy compatibility +- Validates that exactly one argument is passed after schema parsing + +**StructuredTool (structured.py):** +- Multi-argument tool with explicit schema-driven parsing +- Each function parameter becomes a separate schema field (unless injected) +- Supports both `func` (sync) and `coroutine` (async) +- Falls back to executor for sync invocation if no coroutine defined +- Preferred pattern for agent tools with multiple named parameters + +Both inherit from BaseTool and override `_run()` and `_arun()` to delegate to the wrapped function while preserving config and callbacks. + +## Tool Creation: @tool Decorator and Factories + +The `@tool` decorator provides the primary user-facing API for converting functions into tools, with overloads supporting multiple usage patterns. + +**Decorator Forms:** + +```python +# Form 1: No arguments (name from function) +@tool +def search(query: str) -> str: + """Search the API.""" + return f"Results for {query}" + +# Form 2: With parameters +@tool(description="Custom description", return_direct=True) +def calculate(expression: str) -> str: + return str(eval(expression)) + +# Form 3: With explicit name +@tool("my_search") +def search(query: str) -> str: + return query + +# Form 4: With Runnable +tool_obj = tool("math_tool", my_runnable, description="...") +``` + +**Key Behaviors:** + +- Default name is `function.__name__` unless overridden +- Description precedence: explicit param → function docstring → args_schema description +- `parse_docstring=True` extracts Google-style Args sections for parameter descriptions (with validation that documented args match signature) +- `infer_schema=True` (default) automatically generates schema from type hints +- `infer_schema=False` requires explicit description and creates Tool (string-input) instead of StructuredTool +- `response_format="content_and_artifact"` expects function to return `(content, artifact)` tuple + +**Runnable Conversion:** + +When decorating a Runnable, the tool automatically: +- Wraps sync/async invoke methods to inject callbacks +- Uses Runnable's input_schema as the tool's args_schema +- Generates description from input schema if not provided +- Delegates to StructuredTool.from_function() for multi-argument runnables or Tool for string schemas + +**Async Support:** + +The decorator detects coroutines and creates StructuredTool with both `func` and `coroutine` set, enabling true async execution. Mixed sync/async patterns work via the executor fallback. + +## Schema Rendering for Models + +Tools are rendered for language models via utility functions in `render.py`: + +- `render_text_description(tools: list[BaseTool]) -> str` — Returns `"name - description\n..."` format for prompts +- `render_text_description_and_args(tools: list[BaseTool]) -> str` — Includes args: `"name - description, args: {...}"` + +Models receive tool schemas in provider-specific formats (OpenAI function_calling, Anthropic tool_use, etc.), generated by `function_calling.py` utilities that convert tool_call_schema to FunctionDescription dicts with JSON schema parameters. + +The `tool_call_schema` property ensures models never see injected arguments or reserved parameter names, protecting tool implementation details. + +## Advanced Patterns: Injected Arguments and ToolRuntime + +Tools can receive runtime values not controlled by the model via injected arguments. + +**InjectedToolArg:** A marker class for parameters that should be injected at runtime: + +```python +from typing import Annotated +from langchain_core.tools import tool, InjectedToolArg + +@tool +def my_tool( + user_query: str, + context_var: Annotated[str, InjectedToolArg] +) -> str: + # context_var is injected; user only provides user_query + return f"{user_query} in {context_var}" +``` + +**InjectedToolCallId:** Specialized marker to inject the tool_call_id: + +```python +@tool +def track_call(query: str, call_id: InjectedToolCallId) -> str: + # call_id automatically populated with tool_call_id from invocation + return f"Call {call_id}: {query}" +``` + +**ToolRuntime:** A directly-injected argument type providing access to state, context, and store: + +```python +from langchain_core.tools import tool, ToolRuntime + +@tool +def stateful_tool(query: str, runtime: ToolRuntime) -> str: + # Access application state, context, and LangGraph store + state = runtime.state + context = runtime.context + store = runtime.store + return f"State: {state}, Context: {context}" +``` + +Injected arguments are: +- Excluded from tool_call_schema sent to models +- Identified via signature inspection in `_get_injected_args_keys_from_signature()` +- Re-injected during `_parse_input()` from tool metadata or invocation context +- Filtered from callback inputs via `_filter_injected_args()` + +## Error Handling Strategies + +Tools support flexible error handling to allow graceful recovery in agentic loops. + +**ToolException:** Custom exception for controlled tool errors: + +```python +from langchain_core.tools import tool, ToolException + +@tool +def validate_input(value: str) -> str: + if not value: + raise ToolException("Value cannot be empty") + return f"Valid: {value}" +``` + +**Validation Errors:** Pydantic validation failures are caught and handled per `handle_validation_error`: + +```python +@tool(handle_validation_error="Invalid input format") +def my_tool(count: int) -> str: + return f"Count: {count}" + +# If user passes non-integer, returns "Invalid input format" instead of raising +``` + +**Tool Errors:** ToolException handling per `handle_tool_error`: + +```python +@tool(handle_tool_error=True) # Use exception message +def risky_operation() -> str: + raise ToolException("Operation failed") + +# Returns ToolMessage with status="error", content="Operation failed" +``` + +Custom handlers receive the exception and return str or list of message content blocks: + +```python +def my_error_handler(e: ToolException) -> str: + logger.error(f"Tool failed: {e}") + return "Operation failed. Please try again later." + +@tool(handle_tool_error=my_error_handler) +def operation() -> str: + raise ToolException("Internal error") +``` + +Handled errors return ToolMessage with `status="error"` when invoked with `tool_call_id`, allowing agents to observe and respond to failures without breaking the loop. + +## BaseToolkit: Organizing Related Tools + +For complex systems, tools are organized into toolkits via `BaseToolkit`: + +```python +from langchain_core.tools import BaseToolkit, tool + +class MathToolkit(BaseToolkit): + """Toolkit for mathematical operations.""" + + @property + def description(self) -> str: + return "Tools for arithmetic and algebra" + + def get_tools(self) -> list[BaseTool]: + @tool + def add(a: int, b: int) -> int: + return a + b + + @tool + def multiply(a: int, b: int) -> int: + return a * b + + return [add, multiply] + +toolkit = MathToolkit() +tools = toolkit.get_tools() # Retrieve all related tools +``` + +Toolkits enable: +- Logical grouping of related functionality +- Conditional tool availability (return subset based on runtime state) +- Dynamic tool generation +- Integration with agent initialization pipelines + +## Converting Runnables to Tools + +Runnables can be converted to tools via `tool()` decorator or `convert_runnable_to_tool()` function: + +```python +from langchain_core.runnables import RunnablePassthrough +from langchain_core.tools import convert_runnable_to_tool + +my_runnable = RunnablePassthrough() + +# Via convert function +tool_obj = convert_runnable_to_tool( + my_runnable, + name="passthrough", + description="Passes input through unchanged" +) + +# Via decorator +tool_obj = tool("passthrough", my_runnable) +``` + +The conversion: +- Extracts input_schema from Runnable.get_input_jsonschema() +- Validates schema is object type (required for multi-arg tools) +- Wraps invoke/ainvoke to inject callbacks into config +- Delegates to StructuredTool.from_function() with wrapped functions +- Falls back to Tool for string-input runnables + +## Lifecycle and Invariants + +**Tool Instance Lifecycle:** + +1. **Construction:** Schema memoization cleared on `__setattr__` or `model_copy()` if name/description/args_schema changed +2. **First Schema Access:** tool_call_schema builds subset model, patches class to cache JSON schema +3. **Execution:** Callbacks configured, input parsed, injected args identified, function called, output formatted +4. **Pickling:** Schema memo cleared (dynamic classes cannot pickle by reference); rebuilt on next access + +**Schema Caching Invariants:** + +- Memoized subset model class never regenerates if name/description/args_schema unchanged +- Pydantic model_json_schema() called on subset class returns cached dict on subsequent calls +- Cache invalidation is explicit via private _TOOL_CALL_SCHEMA_FIELDS check +- Preserves performance under high-frequency agent loops + +**Execution Invariants:** + +- Callbacks always fire in order: on_tool_start → (on_tool_error | on_tool_end) +- Config context is set during execution, allowing nested tools to access state/store +- ToolMessage wrapping only occurs if tool_call_id provided +- Injected arguments are never visible to the model or in callback inputs +- Status="error" set only when handle_tool_error converts exception to message + +## Extension Points + +**Subclassing BaseTool:** + +Custom tool implementations override: +- `_run(self, *args, **kwargs) -> Any` — Sync execution logic +- `_arun(self, *args, **kwargs) -> Any` — Async execution logic (default delegates to _run via executor) +- `get_input_schema()` — Override schema source (default uses args_schema or creates from _run signature) + +**Custom Error Handlers:** + +Passed as callables to `handle_tool_error` and `handle_validation_error`: + +```python +def custom_validation_handler(e: ValidationError) -> str: + # Extract user-friendly message from Pydantic error + return ", ".join(f"{err['loc'][0]}: {err['msg']}" for err in e.errors()) + +@tool(handle_validation_error=custom_validation_handler) +def my_tool(count: int) -> str: + return str(count) +``` + +**Callback Managers:** + +Tools inject CallbackManager/AsyncCallbackManager to enable: +- Custom event handlers (logging, metrics, tracing) +- Nested tool execution with callback propagation +- on_tool_start/on_tool_end hooks for observability + +Tools expose run_manager in `_run()` signature to allow direct callback invocation. + +## Configuration and Operational Concerns + +**Reserved Parameter Names:** + +Parameters named `config`, `run_manager`, or `callbacks` are filtered from the tool schema because they conflict with LangChain's runtime injection. Use `ToolRuntime` annotation to access runtime state instead. + +**Provider Extras:** + +The `extras` dict allows passing provider-specific configuration: + +```python +@tool(extras={"cache_control": {"type": "ephemeral"}}) +def cached_operation(query: str) -> str: + return query +``` + +Chat models inspect extras and apply provider-specific behavior when rendering tools. + +**Docstring Parsing:** + +When `parse_docstring=True`, Google-style docstrings are parsed for parameter descriptions: + +```python +@tool(parse_docstring=True) +def process(name: str, count: int) -> str: + """Process items by name. + + Args: + name: The item name + count: Number of items to process + """ + return f"{name}: {count}" +``` + +Invalid docstrings (missing Args section, args not in signature, malformed) raise ValueError if `error_on_invalid_docstring=True`. + +**Verbose Output:** + +Set `verbose=True` to log tool execution. Combined with callback managers for comprehensive observability. + +## Summary: When to Use Each Pattern + +- **@tool decorator:** Primary pattern for converting functions to tools; use with type hints for automatic schema inference +- **StructuredTool.from_function():** Direct factory when decorator syntax isn't convenient or for programmatic tool creation +- **Tool (simple):** Legacy compatibility or single-string-input tools +- **BaseToolkit:** Organizing related tools or dynamic tool generation +- **convert_runnable_to_tool():** Wrapping existing Runnables as tools with consistent invocation +- **Injected arguments:** Share runtime context (state, store, call IDs) without model visibility +- **Custom error handlers:** Transform Pydantic or tool errors into user-friendly messages for agents diff --git a/openwiki/unit-tests.md b/openwiki/unit-tests.md new file mode 100644 index 0000000000..544f1d46d0 --- /dev/null +++ b/openwiki/unit-tests.md @@ -0,0 +1,759 @@ +--- +type: "Testing & QA" +title: "Unit Testing: Strategies and Patterns" +description: "How to write unit tests for langchain-core and langchain components using pytest, fixtures, mocking, and standard test classes from langchain-tests." +tags: [unit-tests, pytest, testing, fixtures, mocking, chat-models, tools, embeddings, type-checking, mypy] +verified: + - by: openwiki/0.5.0 + at: 2026-09-03T15:18:34.589Z +sources: + - id: openwiki-source-8f1875229ad4a704c8e20a06 + resource: repo://libs/core/Makefile + - id: openwiki-source-043c2520f819900dc753650e + resource: repo://libs/core/tests/unit_tests/callbacks/test_async_callback_manager.py + - id: openwiki-source-727aef6a92fb635fdbb41cd6 + resource: repo://libs/core/tests/unit_tests/conftest.py + - id: openwiki-source-5e13d2c899eb5925ef28fddf + resource: repo://libs/core/tests/unit_tests/fake/callbacks.py + - id: openwiki-source-e8916b46b41eee662deabd17 + resource: repo://libs/core/tests/unit_tests/fake/test_fake_chat_model.py + - id: openwiki-source-f0e376fe9b6befdcc2505465 + resource: repo://libs/core/tests/unit_tests/pydantic_utils.py + - id: openwiki-source-344bd4b667096c3c45c8fa82 + resource: repo://libs/core/tests/unit_tests/runnables/conftest.py + - id: openwiki-source-4717abc86db20c5c76bbf23a + resource: repo://libs/core/tests/unit_tests/runnables/test_runnable.py + - id: openwiki-source-5839db669f618a6d604790ca + resource: repo://libs/core/tests/unit_tests/stubs.py + - id: openwiki-source-bd29e79613d5f366a00068f5 + resource: repo://libs/standard-tests/langchain_tests/base.py + - id: openwiki-source-3eb9100e02f9d70098d1b30d + resource: repo://libs/standard-tests/langchain_tests/unit_tests/chat_models.py + - id: openwiki-source-54e69c0cb7aa4a73b87cf97d + resource: repo://libs/standard-tests/langchain_tests/unit_tests/embeddings.py + - id: openwiki-source-a6b31954b6df57580d0f3ed0 + resource: repo://libs/standard-tests/langchain_tests/unit_tests/tools.py +generated: { by: "openwiki/0.5.0", at: "2026-09-03T15:18:34.589Z" } +--- + +## Overview + +Unit testing in LangChain covers components in isolation without network calls or external API dependencies. Tests live in `tests/unit_tests/` directories and are run via `make test` or `uv run --group test pytest` with strict socket restrictions and parallelization. + +This page covers the test infrastructure, standard test classes for chat models and tools, common patterns (fixtures, parametrization, mocking, callbacks, snapshot testing), and type checking with mypy. + +## Test Structure and Organization + +### Directory Layout + +Every LangChain package organizes tests consistently: + +``` +libs/core/ +├── tests/ +│ ├── unit_tests/ # No network calls; run via make test +│ ├── integration_tests/ # Live API calls; require credentials and API keys +│ └── benchmarks/ # Performance measurement tests +├── Makefile # Task automation +└── pyproject.toml # Dependencies +``` + +Unit tests mirror the source code structure: a module at `langchain_core/runnables/base.py` has tests in `tests/unit_tests/runnables/test_runnable.py`. + +### Running Unit Tests + +All unit tests are run in parallel with socket restrictions to prevent accidental network access: + +```bash +# Run all unit tests in a package +make test + +# Run a specific test file or directory +make test TEST_FILE=tests/unit_tests/runnables/test_runnable.py + +# Run using uv directly +uv run --group test pytest tests/unit_tests/ + +# Watch mode: auto-rerun on code changes +make test_watch + +# Extended tests (marked with @pytest.mark.requires) +make extended_tests +``` + +The Makefile test target sets `--disable-socket --allow-unix-socket` and uses `pytest-xdist` (`-n auto`) for parallel execution. Environment variables for LangSmith tracing (`LANGCHAIN_TRACING_V2`, `LANGSMITH_API_KEY`, etc.) are explicitly unset to keep tests independent. + +## Standard Test Classes + +The `langchain-tests` package (in `/libs/standard-tests/`) provides reusable base test classes for integrations. These enforce consistent testing across chat models, embeddings, and tools. + +### ChatModelUnitTests + +**Location**: `langchain_tests.unit_tests.ChatModelUnitTests` + +For any chat model, create a test class that inherits from `ChatModelUnitTests` and implements two required properties: + +```python +# tests/unit_tests/test_standard.py +from typing import Type + +import pytest +from langchain_core.language_models import BaseChatModel +from langchain_tests.unit_tests import ChatModelUnitTests + +from my_package.chat_models import MyChatModel + + +class TestMyChatModelUnit(ChatModelUnitTests): + @property + def chat_model_class(self) -> Type[BaseChatModel]: + return MyChatModel + + @property + def chat_model_params(self) -> dict: + return {"model": "my-model-001", "temperature": 0} +``` + +**What It Tests**: +- **Initialization**: Model instantiation with standard parameters +- **Sync/async invoke**: Single message handling in sync and async contexts +- **Streaming**: Chunked streaming responses and chunk accumulation +- **Tool binding**: `bind_tools()` interface (if supported) +- **Structured output**: `with_structured_output()` for schema enforcement (if supported) +- **Serialization**: Dumping and loading the model via LangChain's serialization API +- **Message types**: Single and multi-message conversations, system prompts, tool messages +- **Tool calling**: Correct tool call invocation and result handling (if supported) + +**Configurable Features** (override as properties): + +- `has_tool_calling` (bool): Whether the model's `bind_tools` method is overridden; auto-detected but can be set explicitly +- `has_tool_choice` (bool): Whether `bind_tools` accepts a `tool_choice` parameter for forcing tool calls +- `has_structured_output` (bool): Whether `with_structured_output()` or `bind_tools()` is implemented +- `structured_output_kwargs` (dict): Additional kwargs for `with_structured_output()` (e.g., `{"method": "json_schema"}`) +- `supports_json_mode` (bool): Whether the model supports `method='json_mode'` in structured output +- `supports_image_inputs` (bool): Whether the model accepts image content blocks +- `supports_image_urls` (bool): Whether the model accepts image URLs in content +- `supports_pdf_inputs` (bool): Whether the model accepts PDF file content +- `supports_audio_inputs` (bool): Whether the model accepts audio content +- `returns_usage_metadata` (bool): Whether `invoke()` and `stream()` return usage token counts (default: True) +- `supports_model_override` (bool): Whether the model accepts a `model` parameter in `invoke()` to override at runtime (default: True) +- `model_override_value` (str): Alternative model name for testing dynamic model selection (required if `supports_model_override=True`) + +Example with feature flags: + +```python +class TestOpenAIChatModel(ChatModelUnitTests): + @property + def chat_model_class(self) -> Type[BaseChatModel]: + return ChatOpenAI + + @property + def chat_model_params(self) -> dict: + return {"model": "gpt-4"} + + @property + def has_tool_calling(self) -> bool: + return True + + @property + def structured_output_kwargs(self) -> dict: + return {"method": "json_schema"} + + @property + def supports_image_inputs(self) -> bool: + return True + + @property + def model_override_value(self) -> str: + return "gpt-4-turbo" +``` + +### EmbeddingsUnitTests + +**Location**: `langchain_tests.unit_tests.EmbeddingsUnitTests` + +Test embeddings models similarly: + +```python +from typing import Type + +from langchain_core.embeddings import Embeddings +from langchain_tests.unit_tests import EmbeddingsUnitTests + +from my_package.embeddings import MyEmbeddings + + +class TestMyEmbeddingsUnit(EmbeddingsUnitTests): + @property + def embeddings_class(self) -> Type[Embeddings]: + return MyEmbeddings + + @property + def embedding_model_params(self) -> dict: + return {"model": "embedding-v1"} +``` + +**What It Tests**: +- Model initialization +- Embedding a single text string +- Embedding a batch of text strings +- Initialization from environment variables (if `init_from_env_params` is set) + +**Configurable**: +- `init_from_env_params` (tuple): Return `(env_vars, init_args, expected_attrs)` to test env-based initialization + +### ToolsUnitTests + +**Location**: `langchain_tests.unit_tests.ToolsUnitTests` + +Test custom tools: + +```python +from langchain_core.tools import BaseTool +from langchain_tests.unit_tests import ToolsUnitTests + +from my_package.tools import MyTool + + +class TestMyToolUnit(ToolsUnitTests): + @property + def tool_constructor(self) -> type[BaseTool] | BaseTool: + return MyTool + + @property + def tool_constructor_params(self) -> dict: + return {"api_key": "test-key"} + + @property + def tool_invoke_params_example(self) -> dict: + return {"query": "example query"} +``` + +**What It Tests**: +- Tool initialization +- Tool invocation with example parameters +- Tool schema generation (JSON schema) +- Initialization from environment variables + +## Shared Fixtures and Configuration + +### conftest.py Patterns + +The root `conftest.py` in `tests/unit_tests/` provides shared fixtures and pytest hooks. + +**From `/libs/core/tests/unit_tests/conftest.py`**: + +```python +@pytest.fixture(autouse=True) +def blockbuster() -> Iterator[BlockBuster]: + """Blockbuster fixture prevents blocking I/O in async code.""" + with blockbuster_ctx("langchain_core") as bb: + # Allow blocking in specific functions (e.g., internal API checks) + bb.functions["os.stat"].can_block_in( + "langchain_core/_api/internal.py", "is_caller_internal" + ) + yield bb +``` + +**Custom Markers**: + +```python +def pytest_addoption(parser: pytest.Parser) -> None: + parser.addoption( + "--only-extended", + action="store_true", + help="Only run extended tests marked with @pytest.mark.requires", + ) + parser.addoption( + "--only-core", + action="store_true", + help="Only run core tests (skip extended tests)", + ) + +def pytest_collection_modifyitems(config: pytest.Config, items) -> None: + """Automatically skip tests marked with @pytest.mark.requires if dependencies are missing.""" + for item in items: + requires_marker = item.get_closest_marker("requires") + if requires_marker: + for pkg in requires_marker.args: + if util.find_spec(pkg) is None: + item.add_marker(pytest.mark.skip(reason=f"Requires pkg: {pkg}")) +``` + +**Fixture for Deterministic UUIDs**: + +```python +@pytest.fixture +def deterministic_uuids(mocker): + """Replace random UUIDs with deterministic values for snapshot testing.""" + side_effect = (UUID(f"00000000-0000-4000-8000-{i:012}", version=4) for i in range(10000)) + return mocker.patch("uuid.uuid4", side_effect=side_effect) +``` + +Use the `deterministic_uuids` fixture in tests where UUIDs must be stable across runs: + +```python +def test_runnable_with_trace(deterministic_uuids): + # UUIDs will be predictable now + ... +``` + +### Marker Patterns + +```python +# Skip test if dependency is missing +@pytest.mark.requires("anthropic") +def test_anthropic_tool_calling(): + from anthropic import Anthropic + ... + +# Extended tests (run with make extended_tests or --only-extended) +@pytest.mark.requires("openai") +def test_openai_structured_output(): + ... + +# Parametrized tests +@pytest.mark.parametrize("model_name,expected_tokens", [ + ("small", 100), + ("large", 1000), +]) +def test_model_sizes(model_name, expected_tokens): + ... + +# Skip on Python version +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Requires 3.11+") +def test_new_feature(): + ... + +# Expected failure +@pytest.mark.xfail(reason="Feature not yet implemented") +def test_future_feature(): + ... +``` + +## Fake Implementations for Testing + +LangChain provides fake/mock chat models and other components to avoid API calls in unit tests. + +### FakeChatModel Classes + +Located in `langchain_core.language_models`: + +```python +from langchain_core.language_models import ( + FakeListChatModel, + FakeMessagesListChatModel, + GenericFakeChatModel, + ParrotFakeChatModel, +) +from langchain_core.messages import AIMessage, HumanMessage +``` + +**FakeListChatModel**: Cycles through a fixed list of string responses. + +```python +from langchain_core.language_models import FakeListChatModel + +model = FakeListChatModel(responses=["Hello", "Hi", "Hey"]) +response = model.invoke("How are you?") +# Returns AIMessage(content="Hello") + +response = model.invoke("What's up?") +# Returns AIMessage(content="Hi") +``` + +**GenericFakeChatModel**: Cycles through AIMessage objects; useful for testing streaming. + +```python +from itertools import cycle +from langchain_core.messages import AIMessage +from langchain_core.language_models import GenericFakeChatModel + +messages = cycle([AIMessage(content="response1"), AIMessage(content="response2")]) +model = GenericFakeChatModel(messages=messages) + +# Test streaming +chunks = list(model.stream("query")) +# Chunks are character-level splits of "response1" +``` + +**ParrotFakeChatModel**: Echoes the input message back. + +```python +from langchain_core.language_models import ParrotFakeChatModel + +model = ParrotFakeChatModel() +response = model.invoke("Hello!") +# Returns AIMessage(content="Hello!") +``` + +**FakeListLLM and FakeStreamingListLLM**: Older LLM interface (text-in, text-out). + +```python +from langchain_core.language_models import FakeListLLM + +llm = FakeListLLM(responses=["Response 1", "Response 2"]) +output = llm.invoke("Query") +``` + +### FakeEmbeddings + +```python +from langchain_core.embeddings import FakeEmbeddings + +embeddings = FakeEmbeddings(model="fake-model", size=1536) + +# Embed a single string +vector = embeddings.embed_query("hello") +# Returns a list of 1536 float values (deterministic based on input hash) + +# Embed a batch +vectors = embeddings.embed_documents(["hello", "world"]) +# Returns list of vectors, one per input +``` + +### FakeCallbackHandler + +Located in `tests.unit_tests.fake.callbacks`, a test callback handler that counts events: + +```python +from tests.unit_tests.fake.callbacks import FakeCallbackHandler + +handler = FakeCallbackHandler() + +# Track various events +assert handler.llm_starts == 0 +assert handler.chain_starts == 0 + +# After invoke on a chain with LLM calls: +model.invoke("query", callbacks=[handler]) + +assert handler.llm_starts == 1 +assert handler.llm_ends == 1 +assert handler.starts == 1 # Total starts + +# Fine-grained counters +assert handler.llm_streams == 0 # for streaming models +assert handler.tool_starts == 0 +assert handler.tool_ends == 0 +assert handler.chain_starts == 1 +assert handler.chain_ends == 1 +``` + +## Common Testing Patterns + +### Fixture Usage + +Define reusable components as pytest fixtures: + +```python +import pytest +from langchain_core.messages import HumanMessage, SystemMessage +from langchain_core.prompts import ChatPromptTemplate + +@pytest.fixture +def system_prompt(): + return SystemMessage(content="You are a helpful assistant.") + +@pytest.fixture +def chat_prompt(): + return ChatPromptTemplate.from_messages([ + ("system", "You are a helpful assistant."), + ("human", "{user_input}"), + ]) + +def test_with_prompt(chat_prompt): + # Use the fixture + assert chat_prompt is not None +``` + +Fixtures in `conftest.py` are automatically discovered and available to all tests in that directory and subdirectories. + +### Parametrization + +Test multiple input/output combinations: + +```python +import pytest + +@pytest.mark.parametrize("input_text,expected_length", [ + ("hello", 5), + ("world", 5), + ("testing", 7), + ("", 0), +]) +def test_text_length(input_text, expected_length): + assert len(input_text) == expected_length +``` + +Parametrize with fixtures: + +```python +@pytest.fixture(params=["gpt-3.5-turbo", "gpt-4"]) +def model_name(request): + return request.param + +def test_model_response(model_name): + # Test runs twice, once for each model + model = ChatOpenAI(model=model_name) + response = model.invoke("Hello") + assert response.content is not None +``` + +### Mocking with pytest-mock + +The `pytest-mock` library provides a `mocker` fixture: + +```python +from unittest import mock + +def test_with_mock(mocker): + # Mock a function + mock_api_call = mocker.patch("my_module.api_call") + mock_api_call.return_value = {"status": "success"} + + # Call code that uses api_call + result = my_function() + + assert mock_api_call.called + assert result == {"status": "success"} + + # Check call arguments + mock_api_call.assert_called_with("expected_arg") +``` + +Mock environment variables: + +```python +from unittest import mock +import os + +def test_env_initialization(mocker): + mocker.patch.dict(os.environ, {"API_KEY": "test-key"}) + + # Code that reads API_KEY will get "test-key" + model = MyModel() # reads from os.environ + assert model.api_key == "test-key" +``` + +### Callback Testing + +Test that callbacks are invoked with correct data: + +```python +from langchain_core.callbacks.manager import CallbackManager +from tests.unit_tests.fake.callbacks import FakeCallbackHandler + +def test_callbacks_on_chain(): + handler = FakeCallbackHandler() + + # Create a chain + prompt = ChatPromptTemplate.from_template("Say hello to {name}") + model = FakeChatModel(responses=["Hello Alice"]) + chain = prompt | model + + # Invoke with callbacks + result = chain.invoke( + {"name": "Alice"}, + config={"callbacks": [handler]} + ) + + # Verify callbacks were fired + assert handler.starts == 2 # prompt + model + assert handler.ends == 2 + assert handler.chain_starts == 0 # Only LLM runs were tracked + assert handler.llm_starts == 1 + assert handler.llm_ends == 1 +``` + +### Async Testing + +Mark async tests with `async def` and `pytest` handles them: + +```python +import pytest + +@pytest.mark.asyncio +async def test_async_invoke(): + model = ChatOpenAI() + result = await model.ainvoke("Hello") + assert result.content is not None + +@pytest.mark.asyncio +async def test_async_streaming(): + model = ChatOpenAI() + chunks = [] + async for chunk in model.astream("Hello"): + chunks.append(chunk) + assert len(chunks) > 0 +``` + +### Snapshot Testing with Syrupy + +Snapshot tests capture output and compare against baseline snapshots. Useful for complex structures, traces, and serialized objects. + +```python +from syrupy.assertion import SnapshotAssertion + +def test_runnable_serialization(snapshot: SnapshotAssertion): + prompt = ChatPromptTemplate.from_template("Say {msg}") + model = ChatOpenAI(model="gpt-4") + chain = prompt | model + + # Dump to serializable form + dumped = dumpd(chain) + + # Compare against snapshot + assert dumped == snapshot +``` + +Snapshots are stored in `__snapshots__/` directories. Update them with: + +```bash +make test_watch # Auto-updates snapshots +# or +pytest --snapshot-update +``` + +### Helper Stubs for Message Tests + +When testing messages with generated IDs, use helper functions from `tests.unit_tests.stubs` to match any ID: + +```python +from tests.unit_tests.stubs import ( + _any_id_ai_message, + _any_id_ai_message_chunk, + _any_id_human_message, + AnyStr, +) + +def test_message_response(): + model = GenericFakeChatModel(messages=cycle([AIMessage(content="hello")])) + response = model.invoke("hi") + + # Matches any ID + assert response == _any_id_ai_message(content="hello") + +def test_message_streaming(): + model = GenericFakeChatModel(messages=cycle([AIMessage(content="hello")])) + chunks = list(model.stream("hi")) + + assert chunks[0] == _any_id_ai_message_chunk(content="h") + assert chunks[1] == _any_id_ai_message_chunk(content="ello", chunk_position="last") +``` + +The `AnyStr` class matches any string when used as a value: + +```python +message.id = AnyStr() # Now message.id == any_other_id is True +``` + +## Type Checking with mypy + +Type checking is part of the standard lint workflow: + +```bash +# Full type checking +make type + +# Or directly with mypy +mypy libs/core/langchain_core/ + +# Type check specific file +mypy libs/core/langchain_core/runnables/base.py +``` + +The Makefile runs `mypy` as part of `make lint`, which also runs ruff and format checks: + +```bash +make lint # runs: ruff check, ruff format --diff, mypy +``` + +### Type Checking Patterns + +Use type hints throughout: + +```python +from typing import Any, Sequence +from langchain_core.language_models import BaseChatModel +from langchain_core.messages import BaseMessage + +def create_chain( + model: BaseChatModel, + messages: Sequence[BaseMessage], + temperature: float = 0.7, +) -> str: + """Create and invoke a chain. + + Args: + model: The language model to use. + messages: Input messages. + temperature: Sampling temperature. + + Returns: + The model's response as a string. + """ + response = model.invoke(messages, {"temperature": temperature}) + return response.content +``` + +Handle complex types with `TYPE_CHECKING`: + +```python +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from langchain_tests.unit_tests import ChatModelUnitTests +``` + +Suppress type errors where necessary with comments (sparingly): + +```python +# mypy cannot infer this type from the lambda +my_dict: dict[str, Any] = {} # type: ignore[assignment] + +# Intentional override +result = chain.invoke(message) # type: ignore[return-value] +``` + +## Test Coverage and Reporting + +Generate coverage reports: + +```bash +make coverage + +# Reports generated: +# - coverage.xml (for CI) +# - term-missing (terminal output with uncovered lines) +``` + +## Key Test Infrastructure Files + +- **conftest.py** (`libs/core/tests/unit_tests/conftest.py`): Shared fixtures, markers, blockbuster configuration +- **stubs.py** (`libs/core/tests/unit_tests/stubs.py`): Helper functions for message testing with wildcard IDs +- **pydantic_utils.py** (`libs/core/tests/unit_tests/pydantic_utils.py`): Schema normalization for cross-version Pydantic compatibility +- **fake/callbacks.py** (`libs/core/tests/unit_tests/fake/callbacks.py`): FakeCallbackHandler for tracking events +- **fake/test_fake_chat_model.py**: Examples of testing fake models + +## Best Practices + +1. **Isolate tests**: Each test should be independent and not rely on other tests' state. + +2. **Use fixtures**: Factor out setup code into fixtures for reuse and clarity. + +3. **Mock external dependencies**: Mock API calls, file I/O, and network operations. + +4. **Test behavior, not implementation**: Test what the component does, not how it does it. + +5. **Parametrize to reduce duplication**: Use `@pytest.mark.parametrize` for multiple input cases. + +6. **Snapshot test complex structures**: Use Syrupy for traces, serialized objects, and large outputs. + +7. **Document test intent**: Use clear test names and docstrings. + +8. **Run tests before committing**: Use pre-commit hooks or `make test` locally. + +9. **Type-check as you go**: Run `make lint` or `make type` during development. + +10. **Use markers for categorization**: Mark tests with `@pytest.mark.requires` or custom markers for selective execution.