[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: 🐞 Bug Report\ndescription: File a bug report\ntitle: \"[Bug]: \"\ntype: \"Bug\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for stopping by to let us know something could be better!\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: What happened?\n      description: Also tell us what you expected to happen and how to reproduce the issue.\n      placeholder: Tell us what you see!\n      value: \"A bug happened!\"\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      label: Relevant log output\n      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.\n      render: shell\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Code of Conduct\n      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/MetaGLM/zhipuai-sdk-python-v4/blob/main/CODE_OF_CONDUCT.md)\n      options:\n        - label: I agree to follow this project's Code of Conduct\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "content": "name: 💡 Feature Request\ndescription: Suggest an idea for this repository\ntitle: \"[Feat]: \"\ntype: \"Feature\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for stopping by to let us know something could be better!\n  - type: textarea\n    id: problem\n    attributes:\n      label: Is your feature request related to a problem? Please describe.\n      description: A clear and concise description of what the problem is.\n      placeholder: Ex. I'm always frustrated when [...]\n  - type: textarea\n    id: describe\n    attributes:\n      label: Describe the solution you'd like\n      description: A clear and concise description of what you want to happen.\n    validations:\n      required: true\n  - type: textarea\n    id: alternatives\n    attributes:\n      label: Describe alternatives you've considered\n      description: A clear and concise description of any alternative solutions or features you've considered.\n  - type: textarea\n    id: context\n    attributes:\n      label: Additional context\n      description: Add any other context or screenshots about the feature request here.\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Code of Conduct\n      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/MetaGLM/zhipuai-sdk-python-v4/blob/main/CODE_OF_CONDUCT.md)\n      options:\n        - label: I agree to follow this project's Code of Conduct\n          required: true\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# Description\n\nThank you for opening a Pull Request!\nBefore submitting your PR, there are a few things you can do to make sure it goes smoothly:\n\n- [ ] Follow the [`CONTRIBUTING` Guide](https://github.com/MetaGLM/zhipuai-sdk-python-v4/blob/main/CONTRIBUTING.md).\n- [ ] Make your Pull Request title in the <https://www.conventionalcommits.org/> specification.\n- [ ] Ensure the tests pass (Run `mvn clean test` from the repository root)\n- [ ] Appropriate docs were updated (if necessary)\n\nFixes #<issue_number_goes_here> \n"
  },
  {
    "path": ".github/actions/poetry_setup/action.yml",
    "content": "# An action for setting up poetry install with caching.\n# Using a custom action since the default action does not\n# take poetry install groups into account.\n# Action code from:\n# https://github.com/actions/setup-python/issues/505#issuecomment-1273013236\nname: poetry-install-with-caching\ndescription: Poetry install with support for caching of dependency groups.\n\ninputs:\n  python-version:\n    description: Python version, supporting MAJOR.MINOR only\n    required: true\n\n  poetry-version:\n    description: Poetry version\n    required: true\n\n  cache-key:\n    description: Cache key to use for manual handling of caching\n    required: true\n\n  working-directory:\n    description: Directory whose poetry.lock file should be cached\n    required: true\n\nruns:\n  using: composite\n  steps:\n    - uses: actions/setup-python@v5\n      name: Setup python ${{ inputs.python-version }}\n      id: setup-python\n      with:\n        python-version: ${{ inputs.python-version }}\n\n#    - uses: actions/cache@v4\n#      id: cache-bin-poetry\n#      name: Cache Poetry binary - Python ${{ inputs.python-version }}\n#      env:\n#        SEGMENT_DOWNLOAD_TIMEOUT_MIN: \"1\"\n#      with:\n#        path: |\n#          /opt/pipx/venvs/poetry\n#        # This step caches the poetry installation, so make sure it's keyed on the poetry version as well.\n#        key: bin-poetry-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-${{ inputs.poetry-version }}\n\n    - name: Refresh shell hashtable and fixup softlinks\n      if: steps.cache-bin-poetry.outputs.cache-hit == 'true'\n      shell: bash\n      env:\n        POETRY_VERSION: ${{ inputs.poetry-version }}\n        PYTHON_VERSION: ${{ inputs.python-version }}\n      run: |\n        set -eux\n\n        # Refresh the shell hashtable, to ensure correct `which` output.\n        hash -r\n\n        # `actions/cache@v3` doesn't always seem able to correctly unpack softlinks.\n        # Delete and recreate the softlinks pipx expects to have.\n        rm /opt/pipx/venvs/poetry/bin/python\n        cd /opt/pipx/venvs/poetry/bin\n        ln -s \"$(which \"python$PYTHON_VERSION\")\" python\n        chmod +x python\n        cd /opt/pipx_bin/\n        ln -s /opt/pipx/venvs/poetry/bin/poetry poetry\n        chmod +x poetry\n\n        # Ensure everything got set up correctly.\n        /opt/pipx/venvs/poetry/bin/python --version\n        /opt/pipx_bin/poetry --version\n\n    - name: Install poetry via pip\n      if: steps.cache-bin-poetry.outputs.cache-hit != 'true'\n      shell: bash\n      env:\n        POETRY_VERSION: ${{ inputs.poetry-version }}\n      run: |\n        python -m pip install --upgrade pip\n        python -m pip install \"poetry==$POETRY_VERSION\"\n        poetry --version\n\n    - name: Restore pip and poetry cached dependencies\n      uses: actions/cache@v4\n      env:\n        SEGMENT_DOWNLOAD_TIMEOUT_MIN: \"4\"\n        WORKDIR: ${{ inputs.working-directory == '' && '.' || inputs.working-directory }}\n      with:\n        path: |\n          ~/.cache/pip\n          ~/.cache/pypoetry/virtualenvs\n          ~/.cache/pypoetry/cache\n          ~/.cache/pypoetry/artifacts\n          ${{ env.WORKDIR }}/.venv\n        key: py-deps-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-poetry-${{ inputs.poetry-version }}-${{ inputs.cache-key }}-${{ hashFiles(format('{0}/**/poetry.lock', env.WORKDIR)) }}\n"
  },
  {
    "path": ".github/workflows/_integration_test.yml",
    "content": "name: integration_test\n\non:\n  workflow_dispatch:\n    inputs:\n      working-directory:\n        required: true\n        type: string\n        default: '.'\n        description: \"From which folder this pipeline executes\"\n\nenv:\n  POETRY_VERSION: \"1.8.2\"\n\njobs:\n  build:\n    if: github.ref == 'refs/heads/main'\n    runs-on: ubuntu-latest\n\n    environment: Scheduled testing publish\n    outputs:\n      pkg-name: ${{ steps.check-version.outputs.pkg-name }}\n      version: ${{ steps.check-version.outputs.version }}\n    strategy:\n      matrix:\n        python-version:\n          - \"3.8\"\n          - \"3.9\"\n          - \"3.10\"\n          - \"3.11\"\n          - \"3.12\"\n    name: \"make integration_test #${{ matrix.python-version }}\"\n    steps:\n        - uses: actions/checkout@v4\n\n        - name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}\n          uses: \"./.github/actions/poetry_setup\"\n          with:\n            python-version: ${{ matrix.python-version }}\n            poetry-version: ${{ env.POETRY_VERSION }}\n            working-directory: ${{ inputs.working-directory }}\n            cache-key: core\n\n        - name: Import test dependencies\n          run: poetry install --with test\n          working-directory: ${{ inputs.working-directory }}\n\n        - name: Run integration tests\n          shell: bash\n          env:\n            ZHIPUAI_API_KEY: ${{ secrets.ZHIPUAI_API_KEY }}\n            ZHIPUAI_BASE_URL: ${{ secrets.ZHIPUAI_BASE_URL }}\n          run: |\n            make integration_tests\n\n        - name: Ensure the tests did not create any additional files\n          shell: bash\n          run: |\n            set -eu\n            \n            STATUS=\"$(git status)\"\n            echo \"$STATUS\"\n            \n            # grep will exit non-zero if the target message isn't found,\n            # and `set -e` above will cause the step to fail.\n            echo \"$STATUS\" | grep 'nothing to commit, working tree clean'\n"
  },
  {
    "path": ".github/workflows/_release.yml",
    "content": "name: release\nrun-name: Release ${{ inputs.working-directory }} by @${{ github.actor }}\non:\n  workflow_call:\n    inputs:\n      working-directory:\n        required: true\n        type: string\n        description: \"From which folder this pipeline executes\"\n  workflow_dispatch:\n    inputs:\n      working-directory:\n        required: true\n        type: string\n        default: '.'\n        description: \"From which folder this pipeline executes\"\nenv:\n  PYTHON_VERSION: \"3.9\"\n  POETRY_VERSION: \"1.8.2\"\n\njobs:\n  build:\n    if: github.ref == 'refs/heads/main'\n    environment: Scheduled testing publish\n    runs-on: ubuntu-latest\n\n    outputs:\n      pkg-name: ${{ steps.check-version.outputs.pkg-name }}\n      version: ${{ steps.check-version.outputs.version }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory }}\n          cache-key: release\n\n      # We want to keep this build stage *separate* from the release stage,\n      # so that there's no sharing of permissions between them.\n      # The release stage has trusted publishing and GitHub repo contents write access,\n      # and we want to keep the scope of that access limited just to the release job.\n      # Otherwise, a malicious `build` step (e.g. via a compromised dependency)\n      # could get access to our GitHub or PyPI credentials.\n      #\n      # Per the trusted publishing GitHub Action:\n      # > It is strongly advised to separate jobs for building [...]\n      # > from the publish job.\n      # https://github.com/pypa/gh-action-pypi-publish#non-goals\n      - name: Build project for distribution\n        run: poetry build\n        working-directory: ${{ inputs.working-directory }}\n\n      - name: Upload build\n        uses: actions/upload-artifact@v4\n        with:\n          name: dist\n          path: ${{ inputs.working-directory }}/dist/\n\n      - name: Check Version\n        id: check-version\n        shell: bash\n        working-directory: ${{ inputs.working-directory }}\n        run: |\n          echo pkg-name=\"$(poetry version | cut -d ' ' -f 1)\" >> $GITHUB_OUTPUT\n          echo version=\"$(poetry version --short)\" >> $GITHUB_OUTPUT\n\n  test-pypi-publish:\n    needs:\n      - build\n    uses:\n      ./.github/workflows/_test_release.yml\n    with:\n      working-directory: ${{ inputs.working-directory }}\n    secrets: inherit\n\n  pre-release-checks:\n    needs:\n      - build\n      - test-pypi-publish\n    environment: Scheduled testing publish\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      # We explicitly *don't* set up caching here. This ensures our tests are\n      # maximally sensitive to catching breakage.\n      #\n      # For example, here's a way that caching can cause a falsely-passing test:\n      # - Make the langchain package manifest no longer list a dependency package\n      #   as a requirement. This means it won't be installed by `pip install`,\n      #   and attempting to use it would cause a crash.\n      # - That dependency used to be required, so it may have been cached.\n      #   When restoring the venv packages from cache, that dependency gets included.\n      # - Tests pass, because the dependency is present even though it wasn't specified.\n      # - The package is published, and it breaks on the missing dependency when\n      #   used in the real world.\n\n      - name: Set up Python + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory }}\n\n      - name: Import published package\n        shell: bash\n        working-directory: ${{ inputs.working-directory }}\n        env:\n          PKG_NAME: ${{ needs.build.outputs.pkg-name }}\n          VERSION: ${{ needs.build.outputs.version }}\n        # Here we use:\n        # - The default regular PyPI index as the *primary* index, meaning\n        #   that it takes priority (https://pypi.org/simple)\n        # - The test PyPI index as an extra index, so that any dependencies that\n        #   are not found on test PyPI can be resolved and installed anyway.\n        #   (https://test.pypi.org/simple). This will include the PKG_NAME==VERSION\n        #   package because VERSION will not have been uploaded to regular PyPI yet.\n        # - attempt install again after 5 seconds if it fails because there is\n        #   sometimes a delay in availability on test pypi\n        run: |\n          poetry run pip install \\\n            --extra-index-url https://test.pypi.org/simple/ \\\n            \"$PKG_NAME==$VERSION\" || \\\n          ( \\\n            sleep 5 && \\\n            poetry run pip install \\\n              --extra-index-url https://test.pypi.org/simple/ \\\n              \"$PKG_NAME==$VERSION\" \\\n          )\n\n          # Replace all dashes in the package name with underscores,\n          # since that's how Python imports packages with dashes in the name.\n          IMPORT_NAME=\"$(echo \"$PKG_NAME\" | sed s/-/_/g)\"\n\n          poetry run python -c \"import $IMPORT_NAME; print(dir($IMPORT_NAME))\"\n\n      - name: Import test dependencies\n        run: poetry install --with test\n        working-directory: ${{ inputs.working-directory }}\n\n      # Overwrite the local version of the package with the test PyPI version.\n      - name: Import published package (again)\n        working-directory: ${{ inputs.working-directory }}\n        shell: bash\n        env:\n          PKG_NAME: ${{ needs.build.outputs.pkg-name }}\n          VERSION: ${{ needs.build.outputs.version }}\n        run: |\n          poetry run pip install \\\n            --extra-index-url https://test.pypi.org/simple/ \\\n            \"$PKG_NAME==$VERSION\"\n\n      # - name: Run unit tests\n      #   run: make tests\n      #   env:\n      #     ZHIPUAI_API_KEY: ${{ secrets.ZHIPUAI_API_KEY }}\n      #     ZHIPUAI_BASE_URL: ${{ secrets.ZHIPUAI_BASE_URL }}\n      #   working-directory: ${{ inputs.working-directory }}\n\n      # - name: Run integration tests\n      #   env:\n      #     ZHIPUAI_API_KEY: ${{ secrets.ZHIPUAI_API_KEY }}\n      #     ZHIPUAI_BASE_URL: ${{ secrets.ZHIPUAI_BASE_URL }}\n      #   run: make integration_tests\n      #   working-directory: ${{ inputs.working-directory }}\n\n  publish:\n    needs:\n      - build\n      - test-pypi-publish\n      - pre-release-checks\n    environment: Scheduled testing publish\n    runs-on: ubuntu-latest \n\n    defaults:\n      run:\n        working-directory: ${{ inputs.working-directory }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory }}\n          cache-key: release\n\n      - uses: actions/download-artifact@v4\n        with:\n          name: dist\n          path: ${{ inputs.working-directory }}/dist/\n\n      - name: Publish package distributions to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n       \n        with:\n          packages-dir: ${{ inputs.working-directory }}/dist/\n          verbose: true\n          print-hash: true\n          user: __token__ \n          password: ${{ secrets.PYPI_API_TOKEN }}\n          # We overwrite any existing distributions with the same name and version.\n          # This is *only for CI use* and is *extremely dangerous* otherwise!\n          # https://github.com/pypa/gh-action-pypi-publish#tolerating-release-package-file-duplicates\n          skip-existing: true\n\n  mark-release:\n    needs:\n      - build\n      - test-pypi-publish\n      - pre-release-checks\n      - publish\n    environment: Scheduled testing publish\n    runs-on: ubuntu-latest\n    permissions:\n      # This permission is needed by `ncipollo/release-action` to\n      # create the GitHub release.\n      contents: write\n      id-token: none\n\n    defaults:\n      run:\n        working-directory: ${{ inputs.working-directory }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory }}\n          cache-key: release\n\n      - uses: actions/download-artifact@v4\n        with:\n          name: dist\n          path: ${{ inputs.working-directory }}/dist/\n\n      - name: Create Release\n        uses: ncipollo/release-action@v1\n        if: ${{ inputs.working-directory == '.' }}\n        with:\n          artifacts: \"dist/*\"\n          token: ${{ secrets.GITHUB_TOKEN }}\n          draft: false\n          generateReleaseNotes: true\n          tag: v${{ needs.build.outputs.version }}\n          commit: main\n"
  },
  {
    "path": ".github/workflows/_test.yml",
    "content": "name: test\n\non:\n  workflow_dispatch:\n    inputs:\n      working-directory:\n        required: true\n        type: string\n        default: '.'\n        description: \"From which folder this pipeline executes\"\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - 'action*'\n\nenv:\n  POETRY_VERSION: \"1.8.2\"\n\njobs:\n  build:\n    defaults:\n      run:\n        working-directory: ${{ inputs.working-directory || '.' }}\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version:\n          - \"3.8\"\n          - \"3.9\"\n          - \"3.10\"\n          - \"3.11\"\n          - \"3.12\"\n    name: \"make test #${{ matrix.python-version }}\"\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ matrix.python-version }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory || '.' }}\n          cache-key: core\n\n\n      - name: Import test dependencies\n        run: poetry install --with test\n        working-directory: ${{ inputs.working-directory || '.' }}\n\n      - name: Run core tests\n        shell: bash\n        run: |\n          make test\n\n      - name: Ensure the tests did not create any additional files\n        shell: bash\n        run: |\n          set -eu\n\n          STATUS=\"$(git status)\"\n          echo \"$STATUS\"\n\n          # grep will exit non-zero if the target message isn't found,\n          # and `set -e` above will cause the step to fail.\n          echo \"$STATUS\" | grep 'nothing to commit, working tree clean'\n"
  },
  {
    "path": ".github/workflows/_test_release.yml",
    "content": "name: test-release\n\non:\n  workflow_call:\n    inputs:\n      working-directory:\n        required: true\n        type: string\n        description: \"From which folder this pipeline executes\"\n\nenv:\n  POETRY_VERSION: \"1.8.2\"\n  PYTHON_VERSION: \"3.9\"\n\njobs:\n  build:\n    if: github.ref == 'refs/heads/main'\n    runs-on: ubuntu-latest\n\n    outputs:\n      pkg-name: ${{ steps.check-version.outputs.pkg-name }}\n      version: ${{ steps.check-version.outputs.version }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python + Poetry ${{ env.POETRY_VERSION }}\n        uses: \"./.github/actions/poetry_setup\"\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n          poetry-version: ${{ env.POETRY_VERSION }}\n          working-directory: ${{ inputs.working-directory }}\n          cache-key: release\n\n      # We want to keep this build stage *separate* from the release stage,\n      # so that there's no sharing of permissions between them.\n      # The release stage has trusted publishing and GitHub repo contents write access,\n      # and we want to keep the scope of that access limited just to the release job.\n      # Otherwise, a malicious `build` step (e.g. via a compromised dependency)\n      # could get access to our GitHub or PyPI credentials.\n      #\n      # Per the trusted publishing GitHub Action:\n      # > It is strongly advised to separate jobs for building [...]\n      # > from the publish job.\n      # https://github.com/pypa/gh-action-pypi-publish#non-goals\n      - name: Build project for distribution\n        run: poetry build\n        working-directory: ${{ inputs.working-directory }}\n\n      - name: Upload build\n        uses: actions/upload-artifact@v4\n        with:\n          name: test-dist\n          path: ${{ inputs.working-directory }}/dist/\n\n      - name: Check Version\n        id: check-version\n        shell: bash\n        working-directory: ${{ inputs.working-directory }}\n        run: |\n          echo pkg-name=\"$(poetry version | cut -d ' ' -f 1)\" >> $GITHUB_OUTPUT\n          echo version=\"$(poetry version --short)\" >> $GITHUB_OUTPUT\n\n  publish:\n    needs:\n      - build\n    runs-on: ubuntu-latest\n    environment: Scheduled testing publish\n    # permissions:\n    #   id-token: none  # This is required for requesting the JWT\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/download-artifact@v4\n        with:\n          name: test-dist\n          path: ${{ inputs.working-directory }}/dist/\n\n      - name: Publish to test PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n        with:\n          user: __token__ \n          password: ${{ secrets.TEST_PYPI_API_TOKEN }}\n          packages-dir: ${{ inputs.working-directory }}/dist/\n          verbose: true\n          print-hash: true\n          repository-url: https://test.pypi.org/legacy/ \n          # We overwrite any existing distributions with the same name and version.\n          # This is *only for CI use* and is *extremely dangerous* otherwise!\n          # https://github.com/pypa/gh-action-pypi-publish#tolerating-release-package-file-duplicates\n          skip-existing: true\n"
  },
  {
    "path": ".github/workflows/lint-pr.yaml",
    "content": "name: \"Lint PR\"\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - reopened\n\njobs:\n  lint-pr:\n    name: Validate PR title\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: read\n    steps:\n      - uses: amannn/action-semantic-pull-request@v5\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\n.idea\n*.pyc\n__pycache__/\n.pytest_cache/\n.DS_Store/\n\n*.swp\n*~\n\nbuild/\ndist/\neggs/\n.eggs/\n*.egg-info/\ntest/\nexamples/\n.pypirc\n/poetry.lock\nlogs\n/tests/integration_tests/batchoutput.jsonl\n/tests/integration_tests/content_batchoutput.jsonl\n/tests/integration_tests/write_to_file_batchoutput.jsonl\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of\nexperience, education, socio-economic status, nationality, personal appearance,\nrace, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n*   Using welcoming and inclusive language\n*   Being respectful of differing viewpoints and experiences\n*   Gracefully accepting constructive criticism\n*   Focusing on what is best for the community\n*   Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n*   The use of sexualized language or imagery and unwelcome sexual attention or\n    advances\n*   Trolling, insulting/derogatory comments, and personal or political attacks\n*   Public or private harassment\n*   Publishing others' private information, such as a physical or electronic\n    address, without explicit permission\n*   Other conduct which could reasonably be considered inappropriate in a\n    professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, or to ban temporarily or permanently any\ncontributor for other behaviors that they deem inappropriate, threatening,\noffensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\nThis Code of Conduct also applies outside the project spaces when the Project\nSteward has a reasonable belief that an individual's behavior may have a\nnegative impact on the project or its community.\n\n## Conflict Resolution\n\nWe do not believe that all conflict is bad; healthy debate and disagreement\noften yield positive results. However, it is never okay to be disrespectful or\nto engage in behavior that violates the project’s code of conduct.\n\nIf you see someone violating the code of conduct, you are encouraged to address\nthe behavior directly with those involved. Many issues can be resolved quickly\nand easily, and this gives people more control over the outcome of their\ndispute. If you are unable to resolve the matter for any reason, or if the\nbehavior is threatening or harassing, report it. We are dedicated to providing\nan environment where participants feel welcome and safe.\n\nReports should be directed to *Weijun Zheng (weijun.zheng@aminer.cn)*, the\nProject Steward(s) for *zhipuai-sdk-python-v4*. It is the Project Steward’s duty to\nreceive and address reported violations of the code of conduct. They will then\nwork with a committee consisting of representatives from the Open Source\nPrograms Office and the Z.ai Open Source Strategy team. \n\nWe will investigate every complaint, but you may not receive a direct response.\nWe will use our discretion in determining when and how to follow up on reported\nincidents, which may range from not taking action to permanent expulsion from\nthe project and project-sponsored spaces. We will notify the accused of the\nreport and provide them an opportunity to discuss it before any action is taken.\nThe identity of the reporter will be omitted from the details of the report\nsupplied to the accused. In potentially harmful situations, such as ongoing\nharassment or threats to anyone's safety, we may take action without notice.\n\n## Attribution\n\nThis Code of Conduct is adapted from the Contributor Covenant, version 1.4,\navailable at\nhttps://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to contribute\n\nWe'd love to accept your patches and contributions to this project.\n\n## Contribution process\n\n### Code reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n\n### Contributor Guide\n\nYou may follow these steps to contribute:\n\n1. **Fork the official repository.** This will create a copy of the official repository in your own account.\n2. **Sync the branches.** This will ensure that your copy of the repository is up-to-date with the latest changes from the official repository.\n3. **Work on your forked repository's feature branch.** This is where you will make your changes to the code.\n4. **Commit your updates on your forked repository's feature branch.** This will save your changes to your copy of the repository.\n5. **Submit a pull request to the official repository's main branch.** This will request that your changes be merged into the official repository.\n6. **Resolve any linting errors.** This will ensure that your changes are formatted correctly.\n\nHere are some additional things to keep in mind during the process:\n\n- **Test your changes.** Before you submit a pull request, make sure that your changes work as expected.\n- **Be patient.** It may take some time for your pull request to be reviewed and merged.\n\n\n\n\n\nHave Fun!\n---\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) Zhipu, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: all format lint test tests test_watch integration_tests docker_tests help extended_tests\n\n# Default target executed when no arguments are given to make.\nall: help\n\n######################\n# TESTING AND COVERAGE\n######################\n\n# Define a variable for the test file path.\nTEST_FILE ?= tests/unit_tests/\n\n\ntest tests:\n\tpoetry run pytest --disable-socket --allow-unix-socket $(TEST_FILE)\nintegration_tests:\n\tpoetry run pytest tests/integration_tests\n\n\n######################\n# LINTING AND FORMATTING\n######################\n\n# Define a variable for Python and notebook files.\nPYTHON_FILES=.\nMYPY_CACHE=.mypy_cache\nlint format: PYTHON_FILES=.\nlint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/langchain --name-only --diff-filter=d master | grep -E '\\.py$$|\\.ipynb$$')\nlint_package: PYTHON_FILES=zhipuai\nlint_tests: PYTHON_FILES=tests\nlint_tests: MYPY_CACHE=.mypy_cache_test\n\nlint lint_diff lint_package lint_tests:\n\t./scripts/check_pydantic.sh .\n\t./scripts/lint_imports.sh\n\tpoetry run ruff .\n\t[ \"$(PYTHON_FILES)\" = \"\" ] || poetry run ruff format $(PYTHON_FILES) --diff\n\t[ \"$(PYTHON_FILES)\" = \"\" ] || poetry run ruff --select I $(PYTHON_FILES)\n\t[ \"$(PYTHON_FILES)\" = \"\" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)\n\nformat format_diff:\n\t[ \"$(PYTHON_FILES)\" = \"\" ] || poetry run ruff format $(PYTHON_FILES)\n\t[ \"$(PYTHON_FILES)\" = \"\" ] || poetry run ruff --select I --fix $(PYTHON_FILES)\n\n\n######################\n# HELP\n######################\n\nhelp:\n\t@echo '-- LINTING --'\n\t@echo 'format                       - run code formatters'\n\t@echo 'lint                         - run linters'\n\t@echo '-- TESTS --'\n\t@echo 'test                         - run unit tests'\n\t@echo 'tests                        - run unit tests (alias for \"make test\")'\n\t@echo 'test TEST_FILE=<test_file>   - run all tests in file'\n"
  },
  {
    "path": "README.md",
    "content": "**The [New Python SDK](https://github.com/zai-org/z-ai-sdk-python) for Z.AI and ZhipuAI has released: [z-ai-sdk-python](https://github.com/zai-org/z-ai-sdk-python)!** It is recommended to use this SDK for better, faster, and long-term support.\n\n\n# ZhipuAI Open Platform Python SDK\n\n[![PyPI version](https://img.shields.io/pypi/v/zhipuai.svg)](https://pypi.org/project/zhipuai/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)\n\n[中文文档](README_CN.md) | English\n\nThe official Python SDK for ZhipuAI's large model open interface, making it easier for developers to call ZhipuAI's open APIs.\n\n## ✨ Features\n\n- **Type Safety**: Complete type annotations for all interfaces\n- **Easy Integration**: Simple initialization and intuitive method calls\n- **High Performance**: Built-in connection pooling and request optimization\n- **Secure**: Automatic token caching and secure API key management\n- **Lightweight**: Minimal dependencies with efficient resource usage\n- **Streaming Support**: Real-time streaming responses for chat completions\n\n## 📦 Installation\n\n### Requirements\n\n- **Python**: 3.9+\n- **Package Manager**: pip\n\n### Install via pip\n\n```bash\npip install zhipuai\n```\n\n### Core Dependencies\n\n| Package | Version | Purpose |\n|---------|---------|----------|\n| `httpx` | `>=0.23.0` | HTTP client for API requests |\n| `pydantic` | `>=1.9.0,<3.0.0` | Data validation and serialization |\n| `typing-extensions` | `>=4.0.0` | Enhanced type hints support |\n\n## 🚀 Quick Start\n\n### Basic Usage\n\n```python\nfrom zhipuai import ZhipuAI\n\n# Initialize client\nclient = ZhipuAI(api_key=\"your-api-key\")\n\n# Create chat completion\nresponse = client.chat.completions.create(\n    model=\"glm-4\",\n    messages=[\n        {\"role\": \"user\", \"content\": \"Hello, ZhipuAI!\"}\n    ]\n)\nprint(response.choices[0].message.content)\n```\n\n### Client Configuration\n\n#### Environment Variables\n\n```bash\nexport ZHIPUAI_API_KEY=\"your-api-key\"\nexport ZHIPUAI_BASE_URL=\"https://open.bigmodel.cn/api/paas/v4/\"  # Optional\n```\n\n#### Code Configuration\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(\n    api_key=\"your-api-key\",\n    base_url=\"https://open.bigmodel.cn/api/paas/v4/\"  # Optional\n)\n```\n\n### Advanced Configuration\n\nCustomize client behavior with additional parameters:\n\n```python\nfrom zhipuai import ZhipuAI\nimport httpx\n\nclient = ZhipuAI(\n    api_key=\"your-api-key\",\n    timeout=httpx.Timeout(timeout=300.0, connect=8.0),  # Request timeout\n    max_retries=3,  # Retry attempts\n    base_url=\"https://open.bigmodel.cn/api/paas/v4/\"  # Custom API endpoint\n)\n```\n\n## 📖 Usage Examples\n\n### Basic Chat\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")  # Uses environment variable ZHIPUAI_API_KEY\nresponse = client.chat.completions.create(\n    model=\"glm-4\",\n    messages=[\n        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n        {\"role\": \"user\", \"content\": \"What is artificial intelligence?\"}\n    ],\n    tools=[\n        {\n            \"type\": \"web_search\",\n            \"web_search\": {\n                \"search_query\": \"Search the Zhipu\",\n                \"search_result\": True,\n            }\n        }\n    ],\n    extra_body={\"temperature\": 0.5, \"max_tokens\": 50}\n)\nprint(response)\n```\n\n### Streaming Chat\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nresponse = client.chat.completions.create(\n    model=\"glm-4\",\n    messages=[\n        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n        {\"role\": \"user\", \"content\": \"Tell me a story about AI.\"}\n    ],\n    stream=True\n)\n\nfor chunk in response:\n    if chunk.choices[0].delta.content:\n        print(chunk.choices[0].delta)\n```\n\n### Multimodal Chat\n\n```python\nimport base64\nfrom zhipuai import ZhipuAI\n\ndef encode_image(image_path):\n    \"\"\"Encode image to base64 format\"\"\"\n    with open(image_path, \"rb\") as image_file:\n        return base64.b64encode(image_file.read()).decode('utf-8')\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nbase64_image = encode_image(\"path/to/your/image.jpg\")\n\nresponse = client.chat.completions.create(\n    model=\"glm-4v\",\n    extra_body={\"temperature\": 0.5, \"max_tokens\": 50},\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"text\",\n                    \"text\": \"What's in this image?\"\n                },\n                {\n                    \"type\": \"image_url\",\n                    \"image_url\": {\n                        \"url\": f\"data:image/jpeg;base64,{base64_image}\"\n                    }\n                }\n            ]\n        }\n    ]\n)\nprint(response)\n```\n\n### Character Role-Playing\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nresponse = client.chat.completions.create(\n    model=\"charglm-3\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": \"Hello, how are you doing lately?\"\n        }\n    ],\n    meta={\n        \"user_info\": \"I am a film director who specializes in music-themed movies.\",\n        \"bot_info\": \"You are a popular domestic female singer and actress with outstanding musical talent.\",\n        \"bot_name\": \"Xiaoya\",\n        \"user_name\": \"Director\"\n    }\n)\nprint(response)\n```\n\n### Assistant Conversation\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nresponse = client.assistant.conversation(\n    assistant_id=\"your_assistant_id\", # You can use 65940acff94777010aa6b796 for testing\n    model=\"glm-4-assistant\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [{\n                \"type\": \"text\",\n                \"text\": \"Help me search for the latest ZhipuAI product information\"\n            }]\n        }\n    ],\n    stream=True,\n    attachments=None,\n    metadata=None,\n    request_id=\"request_1790291013237211136\",\n    user_id=\"12345678\"\n)\n\nfor chunk in response:\n    print(chunk)\n```\n\n### Video Generation\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nresponse = client.videos.generations(\n    model=\"cogvideox-2\",\n    prompt=\"A beautiful sunset beach scene\",\n    quality=\"quality\",          # Output mode: use \"quality\" for higher quality, \"speed\" for faster generation\n    with_audio=True,            # Generate video with background audio\n    size=\"1920x1080\",           # Video resolution (up to 4K, e.g. \"3840x2160\")\n    fps=30,                     # Frames per second (choose 30 fps or 60 fps)\n    user_id=\"user_12345\"\n)\n\n# Generation may take some time\nresult = client.videos.retrieve_videos_result(id=response.id)\nprint(result)\n```\n\n## 🚨 Error Handling\n\nThe SDK provides comprehensive error handling:\n\n```python\nfrom zhipuai import ZhipuAI\nimport zhipuai\n\nclient = ZhipuAI()\n\ntry:\n    response = client.chat.completions.create(\n        model=\"glm-4\",\n        messages=[\n            {\"role\": \"user\", \"content\": \"Hello, ZhipuAI!\"}\n        ]\n    )\n    print(response.choices[0].message.content)\n    \nexcept zhipuai.APIStatusError as err:\n    print(f\"API Status Error: {err}\")\nexcept zhipuai.APITimeoutError as err:\n    print(f\"Request Timeout: {err}\")\nexcept Exception as err:\n    print(f\"Other Error: {err}\")\n```\n\n### Error Codes\n\n| Status Code | Error Type | Description |\n|-------------|------------|-------------|\n| 400 | `APIRequestFailedError` | Invalid request parameters |\n| 401 | `APIAuthenticationError` | Authentication failed |\n| 429 | `APIReachLimitError` | Rate limit exceeded |\n| 500 | `APIInternalError` | Internal server error |\n| 503 | `APIServerFlowExceedError` | Server overloaded |\n| N/A | `APIStatusError` | General API error |\n\n## 📈 Version Updates\n\nFor detailed version history and update information, please see [Release-Note.md](Release-Note.md).\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## 📞 Support\n\nFor questions and technical support, please visit [ZhipuAI Open Platform](https://open.bigmodel.cn/) or check our documentation.\n  \n"
  },
  {
    "path": "README_CN.md",
    "content": "Z.AI 和 智谱AI 的 [全新 Python SDK](https://github.com/zai-org/z-ai-sdk-python) 已经发布：[z-ai-sdk-python](https://github.com/zai-org/z-ai-sdk-python)！推荐使用此 SDK，以获得更好、更快的长期支持。\n\n\n# 智谱AI开放平台 Python SDK\n\n[![PyPI version](https://img.shields.io/pypi/v/zhipuai.svg)](https://pypi.org/project/zhipuai/)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)\n\n[English Readme](README.md)\n\n[智谱AI开放平台](https://open.bigmodel.cn/dev/api)官方 Python SDK，帮助开发者快速集成智谱AI强大的人工智能能力到Python应用中。\n\n## ✨ 特性\n\n- 🚀 **类型安全**: 所有接口完全类型封装，无需查阅API文档即可完成接入\n- 🔧 **简单易用**: 简洁直观的API设计，快速上手\n- ⚡ **高性能**: 基于现代Python库构建，性能优异\n- 🛡️ **安全可靠**: 内置身份验证和令牌管理\n- 📦 **轻量级**: 最小化依赖，易于项目集成\n- 🔄 **流式支持**: 支持SSE流式响应和异步调用\n\n## 📦 安装\n\n### 环境要求\n- Python 3.9 或更高版本\n- pip 包管理器\n\n### 使用 pip 安装\n\n```sh\npip install zhipuai\n```\n\n### 📋 核心依赖\n\n本SDK使用以下核心依赖库：\n\n| 依赖库 | 用途 |\n|--------|------|\n| httpx | HTTP客户端库 |\n| pydantic | 数据验证和序列化 |\n| typing-extensions | 类型注解扩展 |\n\n## 🚀 快速开始\n\n### 基本用法\n\n1. **使用API密钥创建客户端**\n2. **调用相应的API方法**\n\n完整示例请参考开放平台[接口文档](https://open.bigmodel.cn/dev/api)以及[使用指南](https://open.bigmodel.cn/dev/howuse/)，记得替换为您自己的API密钥。\n\n### 客户端配置\n\nSDK支持多种方式配置API密钥：\n\n**环境变量配置：**\n```bash\nexport ZHIPUAI_API_KEY=\"your_api_key_here\"\nexport ZHIPUAI_BASE_URL=\"https://open.bigmodel.cn/api/paas/v4/\"  # 可选\n```\n\n**代码配置：**\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(\n    api_key=\"your_api_key_here\",  # 填写您的 APIKey\n) \n```\n**高级配置：**\n\nSDK提供了灵活的客户端配置选项：\n\n```python\nimport httpx\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(\n    api_key=\"your_api_key_here\",\n    timeout=httpx.Timeout(timeout=300.0, connect=8.0),  # 超时配置\n    max_retries=3,  # 重试次数\n    base_url=\"https://open.bigmodel.cn/api/paas/v4/\"  # Custom API endpoint\n)\n```\n\n**配置选项：**\n- `timeout`: 控制接口连接和读取超时时间\n- `max_retries`: 控制重试次数，默认为3次\n- `base_url`: 自定义API基础URL\n\n\n## 💡 使用示例\n\n### 基础对话\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")  # 请填写您自己的APIKey\nresponse = client.chat.completions.create(\n    model=\"glm-4\",  # 填写需要调用的模型名称\n    messages=[\n        {\"role\": \"user\", \"content\": \"你好，请介绍一下智谱AI\"}\n    ],\n    tools=[\n        {\n            \"type\": \"web_search\",\n            \"web_search\": {\n                \"search_query\": \"Search the Zhipu\",\n                \"search_result\": True,\n            }\n        }\n    ],\n    extra_body={\"temperature\": 0.5, \"max_tokens\": 50}\n)\nprint(response.choices[0].message.content)\n```\n\n### 流式对话\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")  # 请填写您自己的APIKey\nresponse = client.chat.completions.create(\n    model=\"glm-4\",  # 填写需要调用的模型名称\n    messages=[\n        {\"role\": \"system\", \"content\": \"你是一个人工智能助手，你叫ChatGLM\"},\n        {\"role\": \"user\", \"content\": \"你好！你叫什么名字\"},\n    ],\n    stream=True,\n)\n\nfor chunk in response:\n    if chunk.choices[0].delta.content:\n        print(chunk.choices[0].delta)\n```\n\n### 多模态对话\n\n```python\nimport base64\nfrom zhipuai import ZhipuAI\n\ndef encode_image(image_path):\n    \"\"\"将图片编码为base64格式\"\"\"\n    with open(image_path, \"rb\") as image_file:\n        return base64.b64encode(image_file.read()).decode('utf-8')\n\nclient = ZhipuAI()  # 请填写您自己的APIKey\nbase64_image = encode_image(\"path/to/your/image.jpg\")\n\nresponse = client.chat.completions.create(\n    model=\"glm-4v\",  # 视觉模型\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [\n                {\n                    \"type\": \"text\",\n                    \"text\": \"请描述这张图片的内容\"\n                },\n                {\n                    \"type\": \"image_url\",\n                    \"image_url\": {\n                        \"url\": f\"data:image/jpeg;base64,{base64_image}\"\n                    }\n                }\n            ]\n        }\n    ],\n    extra_body={\"temperature\": 0.5, \"max_tokens\": 50}\n)\nprint(response.choices[0].message.content)\n```\n\n### 角色扮演\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI()  # 请填写您自己的APIKey\nresponse = client.chat.completions.create(\n    model=\"charglm-3\",  # 角色扮演模型\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": \"你好，最近在忙什么呢？\"\n        }\n    ],\n    meta={\n        \"user_info\": \"我是一位电影导演，擅长拍摄音乐题材的电影。\",\n        \"bot_info\": \"你是一位当红的国内女歌手及演员，拥有出众的音乐才华。\",\n        \"bot_name\": \"小雅\",\n        \"user_name\": \"导演\"\n    },\n)\nprint(response.choices[0].message.content)\n```\n\n### 智能体对话\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI()  # 请填写您自己的APIKey\n\nresponse = client.assistant.conversation(\n    assistant_id=\"your_assistant_id\",  # 智能体ID，可用 65940acff94777010aa6b796 进行测试\n    model=\"glm-4-assistant\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [{\n                \"type\": \"text\",\n                \"text\": \"帮我搜索智谱AI的最新产品信息\"\n            }]\n        }\n    ],\n    stream=True,\n    attachments=None,\n    metadata=None,\n    request_id=\"request_1790291013237211136\",\n    user_id=\"12345678\"\n)\n\nfor chunk in response:\n    print(chunk)\n```\n\n### 视频生成\n\n```python\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI(api_key=\"your-api-key\")\nresponse = client.videos.generations(\n    model=\"cogvideox-2\",\n    prompt=\"一个美丽的日落海滩场景\",   # 生成内容的提示词\n    quality=\"quality\",          # 输出模式：'quality' 表示质量优先，'speed' 表示速度优先\n    with_audio=True,            # 生成带背景音频的视频\n    size=\"1920x1080\",           # 视频分辨率（最高支持 4K，例如 \"3840x2160\"）\n    fps=30,                     # 帧率（可选 30 或 60）\n    user_id=\"user_12345\"\n)\n\n# 生成过程可能需要一些时间\nresult = client.videos.retrieve_videos_result(id=response.id)\nprint(result)\n```\n\n## 🚨 异常处理\n\nSDK提供了完善的异常处理机制：\n\n```python\nfrom zhipuai import ZhipuAI\nimport zhipuai\n\nclient = ZhipuAI()  # 请填写您自己的APIKey\n\ntry:\n    response = client.chat.completions.create(\n        model=\"glm-4\",\n        messages=[\n            {\"role\": \"user\", \"content\": \"你好，智谱AI！\"}\n        ]\n    )\n    print(response.choices[0].message.content)\n    \nexcept zhipuai.APIStatusError as err:\n    print(f\"API状态错误: {err}\")\nexcept zhipuai.APITimeoutError as err:\n    print(f\"请求超时: {err}\")\nexcept Exception as err:\n    print(f\"其他错误: {err}\")\n```\n\n### 错误码说明\n\n| 状态码 | 错误类型 | 说明 |\n|--------|----------|------|\n| 400 | `APIRequestFailedError` | 请求参数错误 |\n| 401 | `APIAuthenticationError` | 身份验证失败 |\n| 429 | `APIReachLimitError` | 请求频率超限 |\n| 500 | `APIInternalError` | 服务器内部错误 |\n| 503 | `APIServerFlowExceedError` | 服务器流量超限 |\n| N/A | `APIStatusError` | 通用API错误 |\n\n## 📈 版本更新\n\n详细的版本更新记录和历史信息，请查看 [Release-Note.md](Release-Note.md)。\n\n## 📄 许可证\n\n本项目基于 MIT 许可证开源 - 详情请查看 [LICENSE](LICENSE) 文件。\n\n## 🤝 贡献\n\n欢迎贡献代码！请随时提交 Pull Request。\n\n## 📞 支持\n\n如有问题和技术支持，请访问 [智谱AI开放平台](https://open.bigmodel.cn/) 或查看我们的文档。\n  \n"
  },
  {
    "path": "Release-Note.md",
    "content": "## Release Notes\n\n### 2024-08-12\n\n**New Features:**\n- Modified video prompt to be optional, added file deletion functionality\n- Added Assistant business logic\n- Fixed embedding 3 dimensions\n\n### 2024-07-25\n\n**Bug Fixes:**\n- Fixed cogvideo related issues\n\n### 2024-07-12\n\n**New Features:**\n- Added advanced search tool Web search business logic\n- Specified Python versions support (3.8, 3.9, 3.10, 3.11, 3.12)\n- Integrated cogvideo business functionality\n\n### 2024-05-20\n\n**Improvements:**\n- Fixed some `python3.12` dependency issues\n- Added pagination processing code, rewrote instantiation rules for some response classes\n- Added type conversion validation\n- Added batch task related APIs\n- Added file stream response wrapper\n\n### 2024-04-29\n\n**Improvements:**\n- Fixed some `python3.7` code compatibility issues\n- Added interface failure retry mechanism, controlled by `retry` parameter with default of 3 retries\n- Adjusted interface timeout strategy, controlled by `Timeout` for interface `connect` and `read` timeout, default `timeout=300.0, connect=8.0`\n- Added support for super-humanoid large model parameters in conversation module, `model=\"charglm-3\"`, `meta` parameter support\n\n### 2024-04-23\n\n**Improvements:**\n- Fixed some compatibility issues with `pydantic<3,>=1.9.0`\n- Message processing business request and response parameters can be extended through configuration\n- Compatible with some parameters `top_p:1`, `temperature:0` (do_sample rewritten to false, parameters top_p temperature do not take effect)\n- Image understanding part, image_url parameter base64 content containing `data:image/jpeg;base64` compatibility\n- Removed JWT authentication logic\n\n---\n\n## Migration Guide\n\nFor users upgrading from older versions, please note the following breaking changes:\n\n### From v3.x to v4.x\n\n- API key configuration has been simplified\n- Some method signatures have changed for better type safety\n- Error handling has been improved with more specific exception types\n\n## Support\n\nFor questions about specific versions or upgrade assistance, please visit our [documentation](https://open.bigmodel.cn/) or contact our support team.\n\n---\n\n## 版本更新\n\n### 2024-08-12\n\n**新功能：**\n- ✅ 视频提示词设为可选，新增文件删除功能\n- ✅ 智能助手业务逻辑\n- 🔧 修复 embedding 3 维度问题\n\n### 2024-07-25\n\n**问题修复：**\n- 🔧 修复 cogvideo 相关问题\n\n### 2024-07-12\n\n**新功能：**\n- ✅ 高级搜索工具 Web search 业务逻辑\n- ✅ 指定 Python 版本支持 (3.8, 3.9, 3.10, 3.11, 3.12)\n- ✅ 集成 cogvideo 业务功能\n\n### 2024-05-20\n\n**改进优化：**\n- 🔧 修复部分 `python3.12` 依赖问题\n- ✅ 新增分页处理代码，重写部分响应类实例化规则\n- ✅ 新增类型转换校验\n- ✅ 批处理任务相关 API\n- ✅ 文件流响应包装器\n\n### 2024-04-29\n\n**改进优化：**\n- 🔧 修复部分 `python3.7` 代码兼容性问题\n- ✅ 接口失败重试机制，通过 `retry` 参数控制重试次数，默认 3 次\n- ⏱️ 调整接口超时策略，通过 `Timeout` 控制接口 `connect` 和 `read` 超时时间，默认 `timeout=300.0, connect=8.0`\n- ✅ 对话模块新增超拟人大模型参数支持，`model=\"charglm-3\"`，`meta` 参数支持\n\n### 2024-04-23\n\n**改进优化：**\n- 🔧 修复部分 `pydantic<3,>=1.9.0` 兼容性问题\n- ✅ 报文处理的业务请求参数和响应参数可通过配置扩充\n- ✅ 兼容部分参数 `top_p:1`，`temperature:0`（do_sample 重写为 false，参数 top_p temperature 不生效）\n- ✅ 图像理解部分，image_url 参数 base64 内容包含 `data:image/jpeg;base64` 兼容性\n- 🔄 删除 JWT 认证逻辑\n\n---\n\n## 迁移指南\n\n对于从旧版本升级的用户，请注意以下重大变更：\n\n### 从 v3.x 到 v4.x\n\n- API 密钥配置已简化\n- 部分方法签名已更改以提供更好的类型安全性\n- 错误处理已改进，提供更具体的异常类型\n\n## 技术支持\n\n如有特定版本问题或升级协助需求，请访问我们的[文档](https://open.bigmodel.cn/)或联系我们的支持团队。"
  },
  {
    "path": "poetry.toml",
    "content": "[virtualenvs]\nin-project = true\n\n[installer]\nmodern-installation = false\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.poetry]\nname = \"zhipuai\"\nversion = \"2.1.5.20250825\"\ndescription = \"A SDK library for accessing big model apis from ZhipuAI\"\nauthors = [\"Zhipu AI\"]\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \">=3.8,<4.0.0,!=3.9.7 \"\nhttpx = \">=0.23.0\"\npydantic = \">=1.9.0,<3.0\"\npydantic-core = \">=2.14.6\"\ncachetools = \">=4.2.2\"\npyjwt = \"~=2.8.0\"\n\n\n[tool.poetry.group.test.dependencies]\n# The only dependencies that should be added are\n# dependencies used for running tests (e.g., pytest, freezegun, response).\n# Any dependencies that do not meet that criteria will be removed.\npytest = \"^7.3.0\"\npytest-cov = \"^4.0.0\"\npytest-dotenv = \"^0.5.2\"\nduckdb-engine = \"^0.9.2\"\npytest-watcher = \"^0.2.6\"\nfreezegun = \"^1.2.2\"\nresponses = \"^0.22.0\"\npytest-asyncio = { version = \"^0.23.2\", python = \"^3.8\" }\nlark = \"^1.1.5\"\npytest-mock = \"^3.10.0\"\npytest-socket = { version = \"^0.6.0\", python = \">=3.8.1,<3.9.7 || >3.9.7,<4.0\" }\nsyrupy = { version = \"^4.0.2\", python = \">=3.8.1,<3.9.7 || >3.9.7,<4.0\" }\nrequests-mock = \"^1.11.0\"\nrespx = \"0.21.1\"\n\n\n[tool.poetry.group.lint]\noptional = true\n\n[tool.poetry.group.lint.dependencies]\nruff = \"^0.1.5\"\n\n[tool.poetry.extras]\ncli = [\"typer\"]\n# An extra used to be able to add extended testing.\n# Please use new-line on formatting to make it easier to add new packages without\n# merge-conflicts\nextended_testing = [\n    \"langchain\",\n]\n\n[tool.ruff.lint]\nselect = [\n    \"E\",    # pycodestyle\n    \"F\",    # pyflakes\n    \"I\",    # isort\n    \"T201\", # print\n]\n\n[tool.ruff]\nline-length = 88\n\n[tool.ruff.format]\nquote-style = \"single\"\nindent-style = \"tab\"\ndocstring-code-format = true\n\n[tool.coverage.run]\nomit = [\n    \"tests/*\",\n]\n\n[build-system]\nrequires = [\"poetry-core>=1.0.0\", \"poetry-plugin-pypi-mirror==0.4.2\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[tool.pytest.ini_options]\n# --strict-markers will raise errors on unknown marks.\n# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks\n#\n# https://docs.pytest.org/en/7.1.x/reference/reference.html\n# --strict-config       any warnings encountered while parsing the `pytest`\n#                       section of the configuration file raise errors.\n#\n# https://github.com/tophat/syrupy\n# --snapshot-warn-unused    Prints a warning on unused snapshots rather than fail the test suite.\naddopts = \"--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv\"\n# Registering custom markers.\n# https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers\nmarkers = [\n    \"requires: mark tests as requiring a specific library\",\n    \"scheduled: mark tests to run in scheduled testing\",\n    \"compile: mark placeholder test used to compile integration tests without running them\"\n]\nasyncio_mode = \"auto\"\n\n\n# https://python-poetry.org/docs/repositories/\n#[[tool.poetry.source]]\n#name = \"tsinghua\"\n#url = \"https://pypi.tuna.tsinghua.edu.cn/simple/\"\n#priority = \"default\"\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "\"\"\"Configuration for unit tests.\"\"\"\n\nfrom importlib import util\nfrom pathlib import Path\nfrom typing import Dict, Sequence\n\nimport pytest\nfrom pytest import Config, Function, Parser\n\nfrom zhipuai.core.logs import (\n\tget_config_dict,\n\tget_log_file,\n\tget_timestamp_ms,\n)\n\n\ndef pytest_addoption(parser: Parser) -> None:\n\t\"\"\"Add custom command line options to pytest.\"\"\"\n\tparser.addoption(\n\t\t'--only-extended',\n\t\taction='store_true',\n\t\thelp='Only run extended tests. Does not allow skipping any extended tests.',\n\t)\n\tparser.addoption(\n\t\t'--only-core',\n\t\taction='store_true',\n\t\thelp='Only run core tests. Never runs any extended tests.',\n\t)\n\n\ndef pytest_collection_modifyitems(config: Config, items: Sequence[Function]) -> None:\n\t\"\"\"Add implementations for handling custom markers.\n\n\tAt the moment, this adds support for a custom `requires` marker.\n\n\tThe `requires` marker is used to denote tests that require one or more packages\n\tto be installed to run. If the package is not installed, the test is skipped.\n\n\tThe `requires` marker syntax is:\n\n\t.. code-block:: python\n\n\t    @pytest.mark.requires('package1', 'package2')\n\t    def test_something(): ...\n\t\"\"\"\n\t# Mapping from the name of a package to whether it is installed or not.\n\t# Used to avoid repeated calls to `util.find_spec`\n\trequired_pkgs_info: Dict[str, bool] = {}\n\n\tonly_extended = config.getoption('--only-extended') or False\n\tonly_core = config.getoption('--only-core') or False\n\n\tif only_extended and only_core:\n\t\traise ValueError('Cannot specify both `--only-extended` and `--only-core`.')\n\n\tfor item in items:\n\t\trequires_marker = item.get_closest_marker('requires')\n\t\tif requires_marker is not None:\n\t\t\tif only_core:\n\t\t\t\titem.add_marker(pytest.mark.skip(reason='Skipping not a core test.'))\n\t\t\t\tcontinue\n\n\t\t\t# Iterate through the list of required packages\n\t\t\trequired_pkgs = requires_marker.args\n\t\t\tfor pkg in required_pkgs:\n\t\t\t\t# If we haven't yet checked whether the pkg is installed\n\t\t\t\t# let's check it and store the result.\n\t\t\t\tif pkg not in required_pkgs_info:\n\t\t\t\t\ttry:\n\t\t\t\t\t\tinstalled = util.find_spec(pkg) is not None\n\t\t\t\t\texcept Exception:\n\t\t\t\t\t\tinstalled = False\n\t\t\t\t\trequired_pkgs_info[pkg] = installed\n\n\t\t\t\tif not required_pkgs_info[pkg]:\n\t\t\t\t\tif only_extended:\n\t\t\t\t\t\tpytest.fail(\n\t\t\t\t\t\t\tf'Package `{pkg}` is not installed but is required for '\n\t\t\t\t\t\t\tf'extended tests. Please install the given package and '\n\t\t\t\t\t\t\tf'try again.',\n\t\t\t\t\t\t)\n\n\t\t\t\t\telse:\n\t\t\t\t\t\t# If the package is not installed, we immediately break\n\t\t\t\t\t\t# and mark the test as skipped.\n\t\t\t\t\t\titem.add_marker(pytest.mark.skip(reason=f'Requires pkg: `{pkg}`'))\n\t\t\t\t\t\tbreak\n\t\telse:\n\t\t\tif only_extended:\n\t\t\t\titem.add_marker(pytest.mark.skip(reason='Skipping not an extended test.'))\n\n\n@pytest.fixture\ndef logging_conf() -> dict:\n\treturn get_config_dict(\n\t\t'info',\n\t\tget_log_file(log_path='logs', sub_dir=f'local_{get_timestamp_ms()}'),\n\t\t1024 * 1024,\n\t\t1024 * 1024 * 1024,\n\t)\n\n\n@pytest.fixture\ndef test_file_path(request) -> Path:\n\tfrom pathlib import Path\n\n\t# 当前执行目录\n\t# 获取当前测试文件的路径\n\ttest_file_path = Path(str(request.fspath)).parent\n\tprint('test_file_path:', test_file_path)\n\treturn test_file_path\n"
  },
  {
    "path": "tests/integration_tests/batchinput.jsonl",
    "content": "{\"custom_id\": \"request-1\", \"method\": \"POST\", \"url\": \"/v4/chat/completions\", \"body\": {\"model\": \"glm-4\", \"messages\": [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},{\"role\": \"user\", \"content\": \"Hello world!\"}],\"max_tokens\": 1000}}\n{\"custom_id\": \"request-2\", \"method\": \"POST\", \"url\": \"/v4/chat/completions\", \"body\": {\"model\": \"glm-4\", \"messages\": [{\"role\": \"system\", \"content\": \"You are an unhelpful assistant.\"},{\"role\": \"user\", \"content\": \"Hello world!\"}],\"max_tokens\": 1000}}"
  },
  {
    "path": "tests/integration_tests/demo.jsonl",
    "content": "{\"messages\": [{\"role\": \"system\", \"content\": \"你是一位乐于助人，知识渊博的全能AI助手。\"},{\"role\": \"user\", \"content\": \"请介绍一下AlphaGo的基本原理。\"}, {\"role\": \"assistant\", \"content\": \"AlphaGo利用深度神经网络与树搜索算法相结合,通过对弈数据进行训练,逐步提升游戏水平,能够对围棋局面进行判断并选择最佳落子位置。\"}]}\n{\"messages\": [{\"role\": \"system\", \"content\": \"你是一位乐于助人，知识渊博的全能AI助手。\"},{\"role\": \"user\", \"content\": \"请列举一些健康饮食的重要原则。\"}, {\"role\": \"assistant\", \"content\": \"健康饮食的重要原则包括:膳食应营养均衡,合理摄入各种营养素;多吃新鲜蔬果,适量饮水;少吃糖、油、盐,忌吃过量快餐;养成规律生活作息习惯,适量运动。\"}]}\n{\"messages\": [{\"role\": \"system\", \"content\": \"你是一位乐于助人，知识渊博的全能AI助手。\"},{\"role\": \"user\", \"content\": \"请给出一些改善睡眠质量的建议。\"}, {\"role\": \"assistant\", \"content\": \"改善睡眠质量的建议包括:保证睡眠时间,避免睡眠不足;睡前放松身心,远离刺激;养成规律作息时间,不要频繁熬夜;适量运动,但不要运动过度;睡前可以喝一杯热牛奶等温和饮料。\"}]}"
  },
  {
    "path": "tests/integration_tests/test.py",
    "content": "import time\n\nfrom zhipuai import ZhipuAI\n\nclient = ZhipuAI()  # 填写您自己的APIKey\n\nresponse = client.videos.generations(\n\tmodel='cogvideo',\n\tprompt='一个年轻的艺术家在一片彩虹上用调色板作画。',\n\t# prompt=\"一只卡通狐狸在森林里跳着欢快的爵士舞。\"\n\t# prompt=\"这是一部汽车广告片，描述了一位30岁的汽车赛车手戴着红色头盔的赛车冒险。背景是蔚蓝的天空和苛刻的沙漠环境，电影风格使用35毫米胶片拍摄，色彩鲜艳夺目。\"\n)\nprint(response)\ntask_id = response.id\ntask_status = response.task_status\nget_cnt = 0\n\nwhile task_status == 'PROCESSING' and get_cnt <= 40:\n\tresult_response = client.videos.retrieve_videos_result(id=task_id)\n\tprint(result_response)\n\ttask_status = result_response.task_status\n\n\ttime.sleep(2)\n\tget_cnt += 1\n"
  },
  {
    "path": "tests/integration_tests/test_agents.py",
    "content": "import logging\nimport logging.config\nimport time\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_sync(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.agents.invoke(\n\t\t\trequest_id=request_id,\n\t\t\tagent_id='general_translation',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_stream(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.agents.invoke(\n\t\t\trequest_id=request_id,\n\t\t\tagent_id='general_translation',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\tuser_id='12345678',\n\t\t\tstream=True,\n\t\t)\n\t\tfor item in response:\n\t\t\tprint(item)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\ndef test_correction():\n\tclient = ZhipuAI()  # 请替换为实际API密钥\n\n\tresponse = client.agents.invoke(\n\t\tagent_id=\"intelligent_education_correction_agent\",\n\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t\"role\": \"user\",\n\t\t\t\t\t\"content\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"image_url\",\n\t\t\t\t\t\t\t\"image_url\": \"https://b0.bdstatic.com/e24937f1f6b9c0ff6895e1012c981515.jpg\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t)\n\tprint(response)\n \ndef test_correction_result(image_id,uuids,trace_id):\n\tclient = ZhipuAI()\n\n\tresponse = client.agents.async_result(\n\t\tagent_id=\"intelligent_education_correction_polling\",\n\t\tcustom_variables={\n\t\t\t\t\"images\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"image_id\": image_id,\n\t\t\t\t\t\t\"uuids\": uuids\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"trace_id\": trace_id\n\t\t}\n\t)\n\tprint(response)\n \ndef main():\n    test_completions_sync()\n    test_completions_stream()\n    # test_correction()\n\t# test_correction_result(image_id,uuids,trace_id)\n\nif __name__ == \"__main__\":\n\tmain()"
  },
  {
    "path": "tests/integration_tests/test_assistant.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_assistant(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tgenerate = client.assistant.conversation(\n\t\t\tassistant_id='659e54b1b8006379b4b2abd6',\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': [{'type': 'text', 'text': '帮我搜索下智谱的cogvideox发布时间'}],\n\t\t\t\t}\n\t\t\t],\n\t\t\tstream=True,\n\t\t\tattachments=None,\n\t\t\tmetadata=None,\n\t\t\trequest_id='request_1790291013237211136',\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tfor assistant in generate:\n\t\t\tprint(assistant)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_assistant_query_support(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.assistant.query_support(\n\t\t\tassistant_id_list=[],\n\t\t\trequest_id='request_1790291013237211136',\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_assistant_query_conversation_usage(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.assistant.query_conversation_usage(\n\t\t\tassistant_id='659e54b1b8006379b4b2abd6',\n\t\t\trequest_id='request_1790291013237211136',\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_translate_api(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\ttranslate_response = client.assistant.conversation(\n\t\t\tassistant_id='9996ijk789lmn012o345p999',\n\t\t\tmessages=[{'role': 'user', 'content': [{'type': 'text', 'text': '你好呀'}]}],\n\t\t\tstream=True,\n\t\t\tattachments=None,\n\t\t\tmetadata=None,\n\t\t\trequest_id='request_1790291013237211136',\n\t\t\tuser_id='12345678',\n\t\t\textra_parameters={'translate': {'from': 'zh', 'to': 'en'}},\n\t\t)\n\t\tfor chunk in translate_response:\n\t\t\tprint(chunk.choices[0].delta)\n\t\t# print(translate_response)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_audio.py",
    "content": "import base64\nimport json\nimport logging\nimport logging.config\nfrom pathlib import Path\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_audio_speech(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tspeech_file_path = Path(__file__).parent / 'speech.wav'\n\t\tresponse = client.audio.speech(\n\t\t\tmodel='cogtts',\n\t\t\tinput='你好呀,欢迎来到智谱开放平台',\n\t\t\tvoice='tongtong',\n\t\t\tstream=False,\n\t\t\tresponse_format='wav',\n\t\t)\n\t\tresponse.stream_to_file(speech_file_path)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\ndef test_audio_speech_streaming(logging_conf):\n    logging.config.dictConfig(logging_conf)  # type: ignore\n    client = ZhipuAI()  # 填写您自己的APIKey\n    try:\n       response = client.audio.speech(\n          model='cogtts',\n          input='你好呀,欢迎来到智谱开放平台',\n          voice='tongtong',\n          stream=True,\n          response_format='mp3',\n          encode_format='hex'\n       )\n       with open(\"output.mp3\", \"wb\") as f:\n          for item in response:\n             choice = item.choices[0]\n             index = choice.index\n             finish_reason = choice.finish_reason\n             audio_delta = choice.delta.content\n             if finish_reason is not None:\n                break\n             f.write(bytes.fromhex(audio_delta))\n             print(f\"audio delta: {audio_delta[:64]}..., 长度:{len(audio_delta)}\")\n    except zhipuai.core._errors.APIRequestFailedError as err:\n       print(err)\n    except zhipuai.core._errors.APIInternalError as err:\n       print(err)\n    except zhipuai.core._errors.APIStatusError as err:\n       print(err)\n    except Exception as e:\n       print(e)\n\n\ndef test_audio_customization(logging_conf):\n\tlogging.config.dictConfig(logging_conf)\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\twith open(Path(__file__).parent / 'speech.wav', 'rb') as file:\n\t\ttry:\n\t\t\tspeech_file_path = Path(__file__).parent / 'speech.wav'\n\t\t\tresponse = client.audio.customization(\n\t\t\t\tmodel='cogtts',\n\t\t\t\tinput='你好呀,欢迎来到智谱开放平台',\n\t\t\t\tvoice_text='这是一条测试用例',\n\t\t\t\tvoice_data=file,\n\t\t\t\tresponse_format='wav',\n\t\t\t)\n\t\t\tresponse.stream_to_file(speech_file_path)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_batches.py",
    "content": "import logging\nimport logging.config\nimport os\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_batch_input_file(test_file_path, logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\n\ttry:\n\t\tbatch_input_file = client.files.create(\n\t\t\tfile=open(os.path.join(test_file_path, 'batchinput.jsonl'), 'rb'),\n\t\t\tpurpose='batch',\n\t\t)\n\n\t\tprint(batch_input_file)\n\n\t#   FileObject(id='20240514_ea19d21b-d256-4586-b0df-e80a45e3c286', bytes=490, created_at=1715673494, filename=None, object='file', purpose='batch', status=None, status_details=None, fileName='batchinput.jsonl')\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_batch_create(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tcreate = client.batches.create(\n\t\t\tinput_file_id='20240514_ea19d21b-d256-4586-b0df-e80a45e3c286',\n\t\t\tendpoint='/v4/chat/completions',\n\t\t\tcompletion_window='24h',\n\t\t\tmetadata={'description': 'job test'},\n\t\t\tauto_delete_input_file=True,\n\t\t)\n\t\tprint(create)\n\t\t# Batch(id='batch_1790292763050508288', completion_window='24h', created_at=1715674031399, endpoint='/v4/chat/completions', input_file_id='20240514_ea19d21b-d256-4586-b0df-e80a45e3c286', object='batch', status='validating', cancelled_at=None, cancelling_at=None, completed_at=None, error_file_id=None, errors=None, expired_at=None, expires_at=None, failed_at=None, finalizing_at=None, in_progress_at=None, metadata={'description': 'job test'}, output_file_id=None, request_counts=BatchRequestCounts(completed=None, failed=None, total=None))\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_batch_retrieve(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tretrieve = client.batches.retrieve('batch_1790291013237211136')\n\t\tprint(retrieve)\n\n\t#   Batch(id='batch_1790291013237211136', completion_window='24h', created_at=1715673614000, endpoint='/v4/chat/completions', input_file_id='20240514_ea19d21b-d256-4586-b0df-e80a45e3c286', object='batch', status='validating', cancelled_at=None, cancelling_at=None, completed_at=None, error_file_id='', errors=None, expired_at=None, expires_at=None, failed_at=None, finalizing_at=None, in_progress_at=None, metadata={'description': 'job test'}, output_file_id='', request_counts=BatchRequestCounts(completed=None, failed=None, total=None))\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_batch_cancel(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tcancel = client.batches.cancel('batch_1790291013237211136')\n\t\tprint(cancel)\n\t\t# Batch(id='batch_1790291013237211136', completion_window='24h', created_at=1715673614000, endpoint='/v4/chat/completions', input_file_id='20240514_ea19d21b-d256-4586-b0df-e80a45e3c286', object='batch', status='cancelling', cancelled_at=None, cancelling_at=1715673698775, completed_at=None, error_file_id='', errors=None, expired_at=None, expires_at=None, failed_at=None, finalizing_at=None, in_progress_at=None, metadata={'description': 'job test'}, output_file_id='', request_counts=BatchRequestCounts(completed=None, failed=None, total=None))\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_batch_list(logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tlist = client.batches.list(limit=10)\n\t\tprint(list)\n\t\tif list.has_more:\n\t\t\tprint('_________get_next_page___________')\n\t\t\tbatch = list.get_next_page()\n\t\t\tprint(batch)\n\t\t\tprint('_________iter_pages___________')\n\t\t\tfor batch in list.iter_pages():\n\t\t\t\tprint(batch)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_batch_result(test_file_path, logging_conf) -> None:\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tcontent = client.files.content('file-QDpVyDIhxj8mcFiduUydNqQN')\n\t\twith open(os.path.join(test_file_path, 'content_batchoutput.jsonl'), 'wb') as f:\n\t\t\tf.write(content.content)\n\t\tcontent.write_to_file(os.path.join(test_file_path, 'write_to_file_batchoutput.jsonl'))\n\n\t\tassert (\n\t\t\tcontent.content == open(os.path.join(test_file_path, 'batchoutput.jsonl'), 'rb').read()\n\t\t)\n\t\tassert (\n\t\t\tcontent.content\n\t\t\t== open(os.path.join(test_file_path, 'write_to_file_batchoutput.jsonl'), 'rb').read()\n\t\t)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_charglm3.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_charglm(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.completions.create(\n\t\t\tmodel='charglm-3',  # 填写需要调用的模型名称\n\t\t\tmessages=[{'role': 'user', 'content': '请问你在做什么'}],\n\t\t\tmeta={\n\t\t\t\t'user_info': '我是陆星辰，是一个男性，是一位知名导演，也是苏梦远的合作导演。我擅长拍摄音乐题材的电影。苏梦远对我的态度是尊敬的，并视我为良师益友。',\n\t\t\t\t'bot_info': '苏梦远，本名苏远心，是一位当红的国内女歌手及演员。在参加选秀节目后，凭借独特的嗓音及出众的舞台魅力迅速成名，进入娱乐圈。她外表美丽动人，但真正的魅力在于她的才华和勤奋。苏梦远是音乐学院毕业的优秀生，善于创作，拥有多首热门原创歌曲。除了音乐方面的成就，她还热衷于慈善事业，积极参加公益活动，用实际行动传递正能量。在工作中，她对待工作非常敬业，拍戏时总是全身心投入角色，赢得了业内人士的赞誉和粉丝的喜爱。虽然在娱乐圈，但她始终保持低调、谦逊的态度，深得同行尊重。在表达时，苏梦远喜欢使用“我们”和“一起”，强调团队精神。',\n\t\t\t\t'bot_name': '苏梦远',\n\t\t\t\t'user_name': '陆星辰',\n\t\t\t},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_async_completions():\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.asyncCompletions.create(\n\t\t\tmodel='charglm',  # 填写需要调用的模型名称\n\t\t\tmessages=[{'role': 'user', 'content': '请问你在做什么'}],\n\t\t\tmeta={\n\t\t\t\t'user_info': '我是陆星辰，是一个男性，是一位知名导演，也是苏梦远的合作导演。我擅长拍摄音乐题材的电影。苏梦远对我的态度是尊敬的，并视我为良师益友。',\n\t\t\t\t'bot_info': '苏梦远，本名苏远心，是一位当红的国内女歌手及演员。在参加选秀节目后，凭借独特的嗓音及出众的舞台魅力迅速成名，进入娱乐圈。她外表美丽动人，但真正的魅力在于她的才华和勤奋。苏梦远是音乐学院毕业的优秀生，善于创作，拥有多首热门原创歌曲。除了音乐方面的成就，她还热衷于慈善事业，积极参加公益活动，用实际行动传递正能量。在工作中，她对待工作非常敬业，拍戏时总是全身心投入角色，赢得了业内人士的赞誉和粉丝的喜爱。虽然在娱乐圈，但她始终保持低调、谦逊的态度，深得同行尊重。在表达时，苏梦远喜欢使用“我们”和“一起”，强调团队精神。',\n\t\t\t\t'bot_name': '苏梦远',\n\t\t\t\t'user_name': '陆星辰',\n\t\t\t},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\n# def test_retrieve_completion_result():\n#     client = ZhipuAI()  # 请填写您自己的APIKey\n#     try:\n#         response = client.chat.asyncCompletions.retrieve_completion_result(id=\"1014908592669352541650991\")\n#         print(response)\n#\n#\n#     except zhipuai.core._errors.APIRequestFailedError as err:\n#         print(err)\n#     except zhipuai.core._errors.APIInternalError as err:\n#         print(err)\n\n# if __name__ == \"__main__\":\n#     test_retrieve_completion_result()\n"
  },
  {
    "path": "tests/integration_tests/test_chat.py",
    "content": "import logging\nimport logging.config\nimport os.path\nimport time\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_temp0(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\ttop_p=0.7,\n\t\t\ttemperature=0,\n\t\t\tmax_tokens=2000,\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_temp1(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\ttop_p=0.7,\n\t\t\ttemperature=1,\n\t\t\tmax_tokens=2000,\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_top0(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\ttop_p=0,\n\t\t\ttemperature=0.9,\n\t\t\tmax_tokens=2000,\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_top1(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\ttop_p=1,\n\t\t\ttemperature=0.9,\n\t\t\tmax_tokens=2000,\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '作为一名营销专家，请为我的产品创作一个吸引人的slogan',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '当然，为了创作一个吸引人的slogan，请告诉我一些关于您产品的信息',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '智谱AI开放平台'},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '智启未来，谱绘无限一智谱AI，让创新触手可及!',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\ttools=[\n\t\t\t\t{\n\t\t\t\t\t'type': 'web_search',\n\t\t\t\t\t'web_search': {\n\t\t\t\t\t\t'search_query': '帮我看看清华的升学率',\n\t\t\t\t\t\t'search_result': True,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t],\n\t\t\tuser_id='12345678',\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_disenable_web_search(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '作为一名营销专家，请为我的产品创作一个吸引人的slogan',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '当然，为了创作一个吸引人的slogan，请告诉我一些关于您产品的信息',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '智谱AI开放平台'},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '智启未来，谱绘无限一智谱AI，让创新触手可及!',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\ttools=[\n\t\t\t\t{\n\t\t\t\t\t'type': 'web_search',\n\t\t\t\t\t'web_search': {\n\t\t\t\t\t\t'search_query': '帮我看看清华的升学率',\n\t\t\t\t\t\t'search_result': True,\n\t\t\t\t\t\t'enable': False,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t],\n\t\t\tuser_id='12345678',\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_enable_web_search(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '作为一名营销专家，请为我的产品创作一个吸引人的slogan',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '当然，为了创作一个吸引人的slogan，请告诉我一些关于您产品的信息',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '智谱AI开放平台'},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '智启未来，谱绘无限一智谱AI，让创新触手可及!',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\ttools=[\n\t\t\t\t{\n\t\t\t\t\t'type': 'web_search',\n\t\t\t\t\t'web_search': {\n\t\t\t\t\t\t'search_query': '帮我看看清华的升学率',\n\t\t\t\t\t\t'search_result': True,\n\t\t\t\t\t\t'enable': True,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t],\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_stream(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',  # 填写需要调用的模型名称\n\t\t\tstream=True,\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '作为一名营销专家，请为我的产品创作一个吸引人的slogan',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '当然，为了创作一个吸引人的slogan，请告诉我一些关于您产品的信息',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '智谱AI开放平台'},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '智启未来，谱绘无限一智谱AI，让创新触手可及!',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tfor item in response:\n\t\t\tprint(item)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\n# Function to encode the image\ndef encode_image(image_path):\n\timport base64\n\n\twith open(image_path, 'rb') as image_file:\n\t\treturn base64.b64encode(image_file.read()).decode('utf-8')\n\n\ndef test_completions_vis(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4v',  # 填写需要调用的模型名称\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': [\n\t\t\t\t\t\t{'type': 'text', 'text': '图里有什么'},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'type': 'image_url',\n\t\t\t\t\t\t\t'image_url': {\n\t\t\t\t\t\t\t\t'url': 'https://img1.baidu.com/it/u=1369931113,3388870256&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703696400&t=f3028c7a1dca43a080aeb8239f09cc2f'\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t],\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_vis_base64(test_file_path, logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tbase64_image = encode_image(os.path.join(test_file_path, 'img/MetaGLM.png'))\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4v',  # 填写需要调用的模型名称\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': [\n\t\t\t\t\t\t{'type': 'text', 'text': '图里有什么'},\n\t\t\t\t\t\t# {\n\t\t\t\t\t\t#     \"type\": \"image_url\",\n\t\t\t\t\t\t#     \"image_url\": {\n\t\t\t\t\t\t#         \"url\": \"https://img1.baidu.com/it/u=1369931113,3388870256&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703696400&t=f3028c7a1dca43a080aeb8239f09cc2f\"\n\t\t\t\t\t\t#     }\n\t\t\t\t\t\t# },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'type': 'image_url',\n\t\t\t\t\t\t\t'image_url': {'url': f'data:image/jpeg;base64,{base64_image}'},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t],\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_async_completions(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.asyncCompletions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '作为一名营销专家，请为我的产品创作一个吸引人的slogan',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '当然，为了创作一个吸引人的slogan，请告诉我一些关于您产品的信息',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '智谱AI开放平台'},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '智启未来，谱绘无限一智谱AI，让创新触手可及!',\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\ttools=[\n\t\t\t\t{\n\t\t\t\t\t'type': 'web_search',\n\t\t\t\t\t'web_search': {\n\t\t\t\t\t\t'search_query': '帮我看看清华的升学率',\n\t\t\t\t\t\t'search_result': True,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t],\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_retrieve_completion_result(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.asyncCompletions.retrieve_completion_result(\n\t\t\tid='1014908592669352541651237'\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_sensitive_word_check(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4',\n\t\t\tmessages=[{'role': 'user', 'content': 'tell me a joke'}],\n\t\t\ttop_p=1,\n\t\t\ttemperature=0.9,\n\t\t\tmax_tokens=2000,\n\t\t\tsensitive_word_check={'type': 'ALL', 'status': 'DISABLE'},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_code_geex.py",
    "content": "import logging\nimport logging.config\nimport time\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_code_geex(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='codegeex-4',\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'system',\n\t\t\t\t\t'content': \"\"\"你是一位智能编程助手，你叫CodeGeeX。你会为用户回答关于编程、代码、计算机方面的任何问题，并提供格式规范、可以执行、准确安全的代码，并在必要时提供详细的解释。\n                    任务：请为输入代码提供格式规范的注释，包含多行注释和单行注释，请注意不要改动原始代码，只需要添加注释。\n                    请用中文回答。\"\"\",\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': \"\"\"写一个快速排序函数\"\"\"},\n\t\t\t],\n\t\t\ttop_p=0.7,\n\t\t\ttemperature=0.9,\n\t\t\tmax_tokens=2000,\n\t\t\tstop=['<|endoftext|>', '<|user|>', '<|assistant|>', '<|observation|>'],\n\t\t\textra={\n\t\t\t\t'target': {\n\t\t\t\t\t'path': '11111',\n\t\t\t\t\t'language': 'Python',\n\t\t\t\t\t'code_prefix': 'EventSource.Factory factory = EventSources.createFactory(OkHttpUtils.getInstance());',\n\t\t\t\t\t'code_suffix': 'TaskMonitorLocal taskMonitorLocal = getTaskMonitorLocal(algoMqReq);',\n\t\t\t\t},\n\t\t\t\t'contexts': [\n\t\t\t\t\t{\n\t\t\t\t\t\t'path': '/1/2',\n\t\t\t\t\t\t'code': 'if(!sensitiveUser){ZpTraceUtils.addAsyncAttribute(algoMqReq.getTaskOrderNo(), ApiTraceProperty.request_params.getCode(), modelSendMap);',\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_code_geex_async(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.asyncCompletions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='codegeex-4',\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'system',\n\t\t\t\t\t'content': \"\"\"你是一位智能编程助手，你叫CodeGeeX。你会为用户回答关于编程、代码、计算机方面的任何问题，并提供格式规范、可以执行、准确安全的代码，并在必要时提供详细的解释。\n                    任务：请为输入代码提供格式规范的注释，包含多行注释和单行注释，请注意不要改动原始代码，只需要添加注释。\n                    请用中文回答。\"\"\",\n\t\t\t\t},\n\t\t\t\t{'role': 'user', 'content': \"\"\"写一个快速排序函数\"\"\"},\n\t\t\t],\n\t\t\ttop_p=0.7,\n\t\t\ttemperature=0.9,\n\t\t\tmax_tokens=2000,\n\t\t\tstop=['<|endoftext|>', '<|user|>', '<|assistant|>', '<|observation|>'],\n\t\t\textra={\n\t\t\t\t'target': {\n\t\t\t\t\t'path': '11111',\n\t\t\t\t\t'language': 'Python',\n\t\t\t\t\t'code_prefix': 'EventSource.Factory factory = EventSources.createFactory(OkHttpUtils.getInstance());',\n\t\t\t\t\t'code_suffix': 'TaskMonitorLocal taskMonitorLocal = getTaskMonitorLocal(algoMqReq);',\n\t\t\t\t},\n\t\t\t\t'contexts': [\n\t\t\t\t\t{\n\t\t\t\t\t\t'path': '/1/2',\n\t\t\t\t\t\t'code': 'if(!sensitiveUser){ZpTraceUtils.addAsyncAttribute(algoMqReq.getTaskOrderNo(), ApiTraceProperty.request_params.getCode(), modelSendMap);',\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_geex_result(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.asyncCompletions.retrieve_completion_result(\n\t\t\tid='1014908807577524653187108'\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_embedding.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_embeddings(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\n\tclient = ZhipuAI()\n\ttry:\n\t\tresponse = client.embeddings.create(\n\t\t\tmodel='embedding-2',  # 填写需要调用的模型名称\n\t\t\tinput='你好',\n\t\t\textra_body={'model_version': 'v1'},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_embeddings_dimensions(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\n\tclient = ZhipuAI()\n\ttry:\n\t\tresponse = client.embeddings.create(\n\t\t\tmodel='embedding-3',  # 填写需要调用的模型名称\n\t\t\tinput='你好',\n\t\t\tdimensions=512,\n\t\t\textra_body={'model_version': 'v1'},\n\t\t)\n\t\tassert response.data[0].object == 'embedding'\n\t\tassert len(response.data[0].embedding) == 512\n\t\tprint(len(response.data[0].embedding))\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_emohaa.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_emohaa(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.completions.create(\n\t\t\tmodel='emohaa',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '你好，我是Emohaa，很高兴见到你。请问有什么我可以帮忙的吗？',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': '今天我休息，决定去西安保密逛逛，心情很好地喷上了我最爱的烟熏木制香',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t'role': 'assistant',\n\t\t\t\t\t'content': '今天我休息，决定去西安保密逛逛，心情很好地喷上了我最爱的烟熏木制香',\n\t\t\t\t},\n\t\t\t],\n\t\t\tmeta={\n\t\t\t\t'user_info': '30岁的男性软件工程师，兴趣包括阅读、徒步和编程',\n\t\t\t\t'bot_info': 'Emohaa是一款基于Hill助人理论的情感支持AI，拥有专业的心理咨询话术能力',\n\t\t\t\t'bot_name': 'Emohaa',\n\t\t\t\t'user_name': '陆星辰',\n\t\t\t},\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_file.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport logging.config\nimport os\n\nimport pytest\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\n@pytest.fixture(scope='class')\ndef test_server():\n\tclass SharedData:\n\t\tclient = ZhipuAI()\n\t\tfile_id1 = None\n\t\tfile_id2 = None\n\n\treturn SharedData()\n\n\nclass TestZhipuAIFileServer:\n\tdef test_logs(self, logging_conf):\n\t\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\n\tdef test_files(self, test_server, test_file_path):\n\t\ttry:\n\t\t\tresult = test_server.client.files.create(\n\t\t\t\tfile=open(os.path.join(test_file_path, 'demo.jsonl'), 'rb'),\n\t\t\t\tpurpose='fine-tune',\n\t\t\t)\n\t\t\tprint(result)\n\t\t\ttest_server.file_id1 = result.id\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_files_validation(self, test_server, test_file_path):\n\t\ttry:\n\t\t\tresult = test_server.client.files.create(\n\t\t\t\tfile=open(os.path.join(test_file_path, 'demo.jsonl'), 'rb'),\n\t\t\t\tpurpose='fine-tune',\n\t\t\t)\n\t\t\tprint(result)\n\n\t\t\ttest_server.file_id2 = result.id\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_files_list(self, test_server):\n\t\ttry:\n\t\t\tlist = test_server.client.files.list()\n\t\t\tprint(list)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_delete_files(self, test_server):\n\t\ttry:\n\t\t\tdelete1 = test_server.client.files.delete(file_id=test_server.file_id1)\n\t\t\tprint(delete1)\n\n\t\t\tdelete2 = test_server.client.files.delete(file_id=test_server.file_id2)\n\t\t\tprint(delete2)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_file_parser.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport logging.config\nimport os\n\nimport pytest\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_file_parser_create(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.file_parser.create(file=open('hitsuyoushorui-cn.pdf', 'rb'), file_type='pdf', tool_type='zhipu_pro')\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\ndef test_file_parser_content(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.file_parser.content(task_id=\"66e8f7ab884448c8b4190f251f6c2982-1\", format_type=\"text\")\n\t\tprint(response.content.decode('utf-8'))\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n"
  },
  {
    "path": "tests/integration_tests/test_finetuning.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_finetuning_create(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tjob = client.fine_tuning.jobs.create(\n\t\t\tmodel='chatglm3-6b',\n\t\t\ttraining_file='file-20240428021923715-xjng4',  # 请填写已成功上传的文件id\n\t\t\tvalidation_file='file-20240428021923715-xjng4',  # 请填写已成功上传的文件id\n\t\t\tsuffix='demo_test',\n\t\t)\n\t\tjob_id = job.id\n\t\tprint(job_id)\n\t\tfine_tuning_job = client.fine_tuning.jobs.retrieve(fine_tuning_job_id=job_id)\n\t\tprint(fine_tuning_job)\n\t\t#     ftjob-20240418110039323-j8lh2\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_finetuning_retrieve(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tfine_tuning_job = client.fine_tuning.jobs.retrieve(\n\t\t\tfine_tuning_job_id='ftjob-20240429112551154-48vq7'\n\t\t)\n\t\tprint(fine_tuning_job)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_finetuning_job_list(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tjob_list = client.fine_tuning.jobs.list()\n\n\t\tprint(job_list)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_finetuning_job_cancel(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tcancel = client.fine_tuning.jobs.cancel(fine_tuning_job_id='ftjob-20240429112551154-48vq7')\n\n\t\tprint(cancel)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_finetuning_job_delete(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 请填写您自己的APIKey\n\ttry:\n\t\tdelete = client.fine_tuning.jobs.delete(fine_tuning_job_id='ftjob-20240126113041678-cs6s9')\n\n\t\tprint(delete)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_model_check(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.chat.completions.create(\n\t\t\tmodel='chatglm3-6b-8572905046912426020-demo_test',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{'role': 'user', 'content': '你是一位乐于助人，知识渊博的全能AI助手。'},\n\t\t\t\t{'role': 'user', 'content': '创造一个更精准、吸引人的slogan'},\n\t\t\t],\n\t\t\textra_body={'temperature': 0.5, 'max_tokens': 50},\n\t\t)\n\t\tprint(response.choices[0].message)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_model_delete(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tdelete = client.fine_tuning.models.delete(\n\t\t\tfine_tuned_model='chatglm3-6b-8572905046912426020-demo_test'\n\t\t)\n\n\t\tprint(delete)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\nif __name__ == '__main__':\n\ttest_finetuning_create()\n"
  },
  {
    "path": "tests/integration_tests/test_images.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_images(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.images.generations(\n\t\t\tmodel='cogview-3',  # 填写需要调用的模型名称\n\t\t\tprompt='一只可爱的小猫咪',\n\t\t\textra_body={'user_id': '1222212'},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_images_sensitive_word_check(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.images.generations(\n\t\t\tmodel='cogview-3',  # 填写需要调用的模型名称\n\t\t\tprompt='一只可爱的小猫咪',\n\t\t\tsensitive_word_check={'type': 'ALL', 'status': 'DISABLE'},\n\t\t\textra_body={'user_id': '1222212'},\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_knowledge.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport logging.config\nimport os\n\nimport pytest\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\n@pytest.fixture(scope='class')\ndef test_server():\n\tclass SharedData:\n\t\tclient = ZhipuAI()\n\t\ttest_knowledge_document_id = None\n\t\ttest_knowledge_id = None\n\n\treturn SharedData()\n\n\nclass TestZhipuAIKnowledgeServer:\n\tdef test_logs(self, logging_conf):\n\t\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\n\tdef test_knowledge_create(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.create(\n\t\t\t\tembedding_id=1,\n\t\t\t\tname='test',\n\t\t\t\tdescription='测试',\n\t\t\t\tbackground='blue',\n\t\t\t\ticon='question',\n\t\t\t)\n\t\t\tprint(result)\n\t\t\ttest_server.test_knowledge_id = result.id\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_document_create(self, test_server, test_file_path):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.document.create(\n\t\t\t\tfile=open(os.path.join(test_file_path, 'file.xlsx'), 'rb'),\n\t\t\t\tpurpose='retrieval',\n\t\t\t\tknowledge_id=test_server.test_knowledge_id,\n\t\t\t\tsentence_size=202,\n\t\t\t)\n\t\t\tprint(result)\n\t\t\ttest_server.test_knowledge_document_id = result.successInfos[0].documentId\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_modify(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.modify(\n\t\t\t\tknowledge_id=test_server.test_knowledge_id,\n\t\t\t\tembedding_id=1,\n\t\t\t\tname='测试1',\n\t\t\t\tbackground='red',\n\t\t\t\ticon='book',\n\t\t\t)\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_query(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.query()\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_used(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.used()\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_document_retrieve(self, test_server, test_file_path):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.document.retrieve(\n\t\t\t\ttest_server.test_knowledge_document_id\n\t\t\t)\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_document_edit(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.document.edit(\n\t\t\t\tdocument_id=test_server.test_knowledge_document_id,\n\t\t\t\tknowledge_type='1',\n\t\t\t\tsentence_size=204,\n\t\t\t)\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_document_list(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.document.list(\n\t\t\t\ttest_server.test_knowledge_id, purpose='retrieval'\n\t\t\t)\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_document_delete(self, test_server):\n\t\ttry:\n\t\t\tfile1 = test_server.client.knowledge.document.delete(\n\t\t\t\ttest_server.test_knowledge_document_id\n\t\t\t)\n\t\t\tprint(file1)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n\n\tdef test_knowledge_delete(self, test_server):\n\t\ttry:\n\t\t\tresult = test_server.client.knowledge.delete(knowledge_id=test_server.test_knowledge_id)\n\t\t\tprint(result)\n\n\t\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIInternalError as err:\n\t\t\tprint(err)\n\t\texcept zhipuai.core._errors.APIStatusError as err:\n\t\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_moderation.py",
    "content": "import logging\nimport logging.config\nimport time\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_temp0(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI(disable_token_cache=False)  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.moderations.create(\n\t\t\tmodel='moderations', input={'type': 'text', 'text': 'hello world '}\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_tools.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_tools(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.tools.web_search(\n\t\t\tmodel='web-search-pro',\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'content': '你好',\n\t\t\t\t\t'role': 'user',\n\t\t\t\t}\n\t\t\t],\n\t\t\tstream=False,\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_tools_stream(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.tools.web_search(\n\t\t\tmodel='web-search-pro',\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'content': '你好',\n\t\t\t\t\t'role': 'user',\n\t\t\t\t}\n\t\t\t],\n\t\t\tstream=True,\n\t\t)\n\t\tfor item in response:\n\t\t\tprint(item)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_transcriptions.py",
    "content": "import logging\nimport logging.config\nfrom pathlib import Path\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_transcriptions(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\twith open(Path(__file__).parent / 'asr1.wav', 'rb') as audio_file:\n\t\t\ttranscriptResponse = client.audio.transcriptions.create(\n\t\t\t\tmodel='glm-asr', file=audio_file, stream=False\n\t\t\t)\n\t\t\tprint(transcriptResponse)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_transcriptions_stream(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\twith open(Path(__file__).parent / 'asr1.wav', 'rb') as audio_file:\n\t\t\ttranscriptResponse = client.audio.transcriptions.create(\n\t\t\t\tmodel='glm-asr', file=audio_file, stream=True\n\t\t\t)\n\t\t\tfor item in transcriptResponse:\n\t\t\t\tprint(item)\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_videos.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_videos(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.videos.generations(\n\t\t\tmodel='cogvideox', prompt='一个开船的人', user_id='1212222'\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_videos_sensitive_word_check(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.videos.generations(\n\t\t\tmodel='cogvideo',\n\t\t\tprompt='一个开船的人',\n\t\t\tsensitive_word_check={'type': 'ALL', 'status': 'DISABLE'},\n\t\t\tuser_id='1212222',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_videos_image_url(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.videos.generations(\n\t\t\tmodel='cogvideo',\n\t\t\timage_url='https://cdn.bigmodel.cn/static/platform/images/solutions/car/empowerment/icon-metric.png',\n\t\t\tprompt='一些相信光的人，举着奥特曼',\n\t\t\tuser_id='12222211',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_retrieve_videos_result(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.videos.retrieve_videos_result(id='1014908869548405238276203')\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_vlm_thinking.py",
    "content": "import logging\nimport logging.config\nimport time\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_completions_vlm_thinking(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4.1v-thinking-flash',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': [\n\t\t\t\t\t\t{'type': 'text', 'text': '图里有什么'},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'type': 'image_url',\n\t\t\t\t\t\t\t'image_url': {\n\t\t\t\t\t\t\t\t'url': 'https://img1.baidu.com/it/u=1369931113,3388870256&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703696400&t=f3028c7a1dca43a080aeb8239f09cc2f'\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t],\n\t\t\ttemperature=0.5,\n\t\t\tmax_tokens=1024,\n\t\t\tuser_id='12345678',\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n\n\ndef test_completions_vlm_thinking_stream(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\t# 生成request_id\n\t\trequest_id = time.time()\n\t\tprint(f'request_id:{request_id}')\n\t\tresponse = client.chat.completions.create(\n\t\t\trequest_id=request_id,\n\t\t\tmodel='glm-4.1v-thinking-flash',  # 填写需要调用的模型名称\n\t\t\tmessages=[\n\t\t\t\t{\n\t\t\t\t\t'role': 'user',\n\t\t\t\t\t'content': [\n\t\t\t\t\t\t{'type': 'text', 'text': '图里有什么'},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t'type': 'image_url',\n\t\t\t\t\t\t\t'image_url': {\n\t\t\t\t\t\t\t\t'url': 'https://img1.baidu.com/it/u=1369931113,3388870256&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703696400&t=f3028c7a1dca43a080aeb8239f09cc2f'\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t],\n\t\t\ttemperature=0.5,\n\t\t\tmax_tokens=1024,\n\t\t\tuser_id='12345678',\n\t\t\tstream=True,\n\t\t)\n\t\tfor item in response:\n\t\t\tprint(item)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/integration_tests/test_web_search.py",
    "content": "import logging\nimport logging.config\n\nimport zhipuai\nfrom zhipuai import ZhipuAI\n\n\ndef test_web_search(logging_conf):\n\tlogging.config.dictConfig(logging_conf)  # type: ignore\n\tclient = ZhipuAI()  # 填写您自己的APIKey\n\ttry:\n\t\tresponse = client.web_search.web_search(\n\t\t\tsearch_engine='search-std',\n\t\t\tsearch_query='2025特朗普向中国加征了多少关税',\n\t\t\tcount=50,\n\t\t\tsearch_domain_filter='finance.sina.com.cn',\n\t\t\tsearch_recency_filter='oneYear',\n\t\t\tcontent_size='high',\n\t\t\tsearch_intent=True,\n\t\t)\n\t\tprint(response)\n\n\texcept zhipuai.core._errors.APIRequestFailedError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIInternalError as err:\n\t\tprint(err)\n\texcept zhipuai.core._errors.APIStatusError as err:\n\t\tprint(err)\n"
  },
  {
    "path": "tests/unit_tests/batchinput.jsonl",
    "content": "{\"custom_id\": \"request-1\", \"method\": \"POST\", \"url\": \"/v4/chat/completions\", \"body\": {\"model\": \"glm-4\", \"messages\": [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},{\"role\": \"user\", \"content\": \"Hello world!\"}],\"max_tokens\": 1000}}"
  },
  {
    "path": "tests/unit_tests/maybe/test_maybe_transform.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zhipuai.core import maybe_transform\nfrom zhipuai.types import batch_create_params\n\n\ndef test_response_joblist_model_cast() -> None:\n\tparams = maybe_transform(\n\t\t{\n\t\t\t'completion_window': '/v1/chat/completions',\n\t\t\t'endpoint': None,\n\t\t\t'metadata': {'key': 'value'},\n\t\t},\n\t\tbatch_create_params.BatchCreateParams,\n\t)\n\tassert isinstance(params, dict)\n"
  },
  {
    "path": "tests/unit_tests/response_model/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit_tests/response_model/test_response.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Type\n\nimport httpx\nimport pytest\n\nfrom zhipuai.core import BaseModel, HttpClient\nfrom zhipuai.core._base_type import ResponseT\nfrom zhipuai.core._request_opt import FinalRequestOptions\nfrom zhipuai.core._response import APIResponse\nfrom zhipuai.types.chat.async_chat_completion import (\n\tAsyncCompletion,\n\tAsyncTaskStatus,\n)\nfrom zhipuai.types.chat.chat_completion import (\n\tCompletion,\n)\nfrom zhipuai.types.chat.chat_completion import (\n\tCompletionChoice as ChatCompletionChoice,\n)\nfrom zhipuai.types.chat.chat_completion import (\n\tCompletionMessageToolCall as ChatCompletionMessageToolCall,\n)\nfrom zhipuai.types.chat.chat_completion import (\n\tCompletionUsage as ChatCompletionUsage,\n)\nfrom zhipuai.types.embeddings import Embedding, EmbeddingsResponded\nfrom zhipuai.types.files.file_object import FileObject, ListOfFileObject\nfrom zhipuai.types.fine_tuning import FineTuningJobEvent\nfrom zhipuai.types.fine_tuning.fine_tuning_job import Error, FineTuningJob\nfrom zhipuai.types.fine_tuning.fine_tuning_job import (\n\tHyperparameters as FineTuningHyperparameters,\n)\nfrom zhipuai.types.fine_tuning.fine_tuning_job_event import JobEvent, Metric\nfrom zhipuai.types.fine_tuning.models import FineTunedModelsStatus\nfrom zhipuai.types.image import GeneratedImage, ImagesResponded\n\n\nclass MockClient:\n\t_strict_response_validation: bool = False\n\n\tdef _process_response_data(\n\t\tself,\n\t\t*,\n\t\tdata: object,\n\t\tcast_type: Type[ResponseT],\n\t\tresponse: httpx.Response,\n\t) -> ResponseT:\n\t\tpass\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tAsyncTaskStatus,\n\t\tAsyncCompletion,\n\t\tCompletion,\n\t],\n)\ndef test_response_chat_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n    \"id\": \"completion123\",\n    \"request_id\": \"request456\",\n    \"model\": \"model-name\",\n    \"task_status\": \"completed\",\n    \"choices\": [\n      {\n        \"index\": 0,\n        \"finish_reason\": \"normal\",\n        \"message\": {\n          \"content\": \"This is the completion content.\",\n          \"role\": \"assistant\",\n          \"tool_calls\": [\n            {\n              \"id\": \"toolcall789\",\n              \"function\": {\n                \"arguments\": \"arg1, arg2\",\n                \"name\": \"functionName\"\n              },\n              \"type\": \"function_call\"\n            }\n          ]\n        }\n      }\n    ],\n    \"usage\": {\n      \"prompt_tokens\": 10,\n      \"completion_tokens\": 15,\n      \"total_tokens\": 25\n    }\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tif R == AsyncTaskStatus:\n\t\tassert R == model.__class__\n\t\tassert isinstance(model, AsyncTaskStatus)\n\t\tassert model.id == 'completion123'\n\t\tassert model.request_id == 'request456'\n\t\tassert model.model == 'model-name'\n\t\tassert model.task_status == 'completed'\n\n\telif R == AsyncCompletion:\n\t\tassert R == model.__class__\n\t\tassert isinstance(model, AsyncCompletion)\n\t\tassert model.id == 'completion123'\n\t\tassert model.request_id == 'request456'\n\t\tassert model.model == 'model-name'\n\t\tassert model.task_status == 'completed'\n\t\tassert isinstance(model.choices, list)\n\t\tassert model.choices[0].index == 0\n\t\tassert model.choices[0].finish_reason == 'normal'\n\t\tassert (\n\t\t\tmodel.choices[0].message.content\n\t\t\t== 'This is the completion content.'\n\t\t)\n\t\tassert model.choices[0].message.role == 'assistant'\n\t\tassert isinstance(model.choices[0].message.tool_calls, list)\n\t\tassert model.choices[0].message.tool_calls[0].id == 'toolcall789'\n\t\tassert (\n\t\t\tmodel.choices[0].message.tool_calls[0].function.arguments\n\t\t\t== 'arg1, arg2'\n\t\t)\n\t\tassert (\n\t\t\tmodel.choices[0].message.tool_calls[0].function.name\n\t\t\t== 'functionName'\n\t\t)\n\t\tassert model.choices[0].message.tool_calls[0].type == 'function_call'\n\t\tassert model.usage.prompt_tokens == 10\n\t\tassert model.usage.completion_tokens == 15\n\t\tassert model.usage.total_tokens == 25\n\telif R == Completion:\n\t\tassert R == model.__class__\n\t\tassert isinstance(model, Completion)\n\t\tassert model.id == 'completion123'\n\t\tassert model.request_id == 'request456'\n\t\tassert model.model == 'model-name'\n\t\tassert model.created == None\n\t\tassert isinstance(model.choices, list)\n\t\tassert isinstance(model.choices[0], ChatCompletionChoice)\n\t\tassert model.choices[0].index == 0\n\t\tassert model.choices[0].finish_reason == 'normal'\n\t\tassert (\n\t\t\tmodel.choices[0].message.content\n\t\t\t== 'This is the completion content.'\n\t\t)\n\t\tassert model.choices[0].message.role == 'assistant'\n\t\tassert isinstance(model.choices[0].message.tool_calls, list)\n\t\tassert isinstance(\n\t\t\tmodel.choices[0].message.tool_calls[0],\n\t\t\tChatCompletionMessageToolCall,\n\t\t)\n\t\tassert model.choices[0].message.tool_calls[0].id == 'toolcall789'\n\t\tassert (\n\t\t\tmodel.choices[0].message.tool_calls[0].function.arguments\n\t\t\t== 'arg1, arg2'\n\t\t)\n\t\tassert (\n\t\t\tmodel.choices[0].message.tool_calls[0].function.name\n\t\t\t== 'functionName'\n\t\t)\n\t\tassert model.choices[0].message.tool_calls[0].type == 'function_call'\n\t\tassert isinstance(model.usage, ChatCompletionUsage)\n\t\tassert model.usage.prompt_tokens == 10\n\t\tassert model.usage.completion_tokens == 15\n\t\tassert model.usage.total_tokens == 25\n\n\telse:\n\t\tassert False, f'Unexpected model type: {R}'\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tFineTunedModelsStatus,\n\t],\n)\ndef test_response_finetuned_model_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n    \"request_id\": \"12345\",\n    \"model_name\": \"my-fine-tuned-model\",\n    \"delete_status\": \"deleted\"\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model, FineTunedModelsStatus)\n\tassert model.request_id == '12345'\n\tassert model.model_name == 'my-fine-tuned-model'\n\tassert model.delete_status == 'deleted'\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tFineTuningJob,\n\t],\n)\ndef test_response_job_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\" {\n    \"id\": \"job123\",\n    \"request_id\": \"req456\",\n    \"created_at\": 1617181723,\n    \"error\": {\n      \"code\": \"404\",\n      \"message\": \"Not Found\",\n      \"param\": \"model_id\"\n    },\n    \"fine_tuned_model\": \"ft_model_1\",\n    \"finished_at\": 1617182000,\n    \"hyperparameters\": {\n      \"n_epochs\": 10\n    },\n    \"model\": \"base_model\",\n    \"object\": \"fine_tuning_job\",\n    \"result_files\": [\n      \"result1.txt\",\n      \"result2.json\"\n    ],\n    \"status\": \"completed\",\n    \"trained_tokens\": 1000000,\n    \"training_file\": \"training_data.csv\",\n    \"validation_file\": \"validation_data.csv\"\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model, FineTuningJob)\n\tassert model.id == 'job123'\n\tassert model.request_id == 'req456'\n\tassert model.created_at == 1617181723\n\tassert isinstance(model.error, Error)\n\tassert model.error.code == '404'\n\tassert model.error.message == 'Not Found'\n\tassert model.error.param == 'model_id'\n\tassert model.fine_tuned_model == 'ft_model_1'\n\tassert model.finished_at == 1617182000\n\tassert isinstance(model.hyperparameters, FineTuningHyperparameters)\n\tassert model.hyperparameters.n_epochs == 10\n\tassert model.model == 'base_model'\n\tassert model.object == 'fine_tuning_job'\n\tassert model.result_files == ['result1.txt', 'result2.json']\n\tassert model.status == 'completed'\n\tassert model.trained_tokens == 1000000\n\tassert model.training_file == 'training_data.csv'\n\tassert model.validation_file == 'validation_data.csv'\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tFineTuningJobEvent,\n\t],\n)\ndef test_response_joblist_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n    \"object\": \"fine_tuning_job\",\n    \"data\": [\n      {\n        \"object\": \"job_event\",\n        \"id\": \"event123\",\n        \"type\": \"training\",\n        \"created_at\": 1617181723,\n        \"level\": \"info\",\n        \"message\": \"Training has started.\",\n        \"data\": {\n          \"epoch\": 1,\n          \"current_steps\": 100,\n          \"total_steps\": 1000,\n          \"elapsed_time\": \"00:10:00\",\n          \"remaining_time\": \"05:20:00\",\n          \"trained_tokens\": 500000,\n          \"loss\": 0.05,\n          \"eval_loss\": 0.03,\n          \"acc\": 0.9,\n          \"eval_acc\": 0.95,\n          \"learning_rate\": 0.001\n        }\n      }\n    ],\n    \"has_more\": false\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model, FineTuningJobEvent)\n\tassert isinstance(model.data, list)\n\tassert isinstance(model.data[0], JobEvent)\n\tassert model.data[0].object == 'job_event'\n\tassert model.data[0].id == 'event123'\n\tassert model.data[0].type == 'training'\n\tassert model.data[0].created_at == 1617181723\n\tassert model.data[0].level == 'info'\n\tassert model.data[0].message == 'Training has started.'\n\tassert isinstance(model.data[0].data, Metric)\n\tassert model.data[0].data.epoch == 1\n\tassert model.data[0].data.current_steps == 100\n\tassert model.data[0].data.total_steps == 1000\n\tassert model.data[0].data.elapsed_time == '00:10:00'\n\tassert model.data[0].data.remaining_time == '05:20:00'\n\tassert model.data[0].data.trained_tokens == 500000\n\tassert model.data[0].data.loss == 0.05\n\tassert model.data[0].data.eval_loss == 0.03\n\tassert model.data[0].data.acc == 0.9\n\tassert model.data[0].data.eval_acc == 0.95\n\tassert model.data[0].data.learning_rate == 0.001\n\tassert model.has_more == False\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[EmbeddingsResponded],\n)\ndef test_response_embedding_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n    \"object\": \"embeddings\",\n    \"data\": [\n      {\n        \"object\": \"embedding\",\n        \"index\": 1,\n        \"embedding\": [0.1, 0.2] \n      }\n    ],\n    \"model\": \"some-model-name\",\n    \"usage\": {\n      \"prompt_tokens\": 20,\n      \"completion_tokens\": 30,\n      \"total_tokens\": 50\n    }\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model, EmbeddingsResponded)\n\tassert isinstance(model.data, list)\n\tassert isinstance(model.data[0], Embedding)\n\tassert model.data[0].object == 'embedding'\n\tassert model.data[0].index == 1\n\tassert model.data[0].embedding == [0.1, 0.2]\n\tassert model.object == 'embeddings'\n\tassert model.model == 'some-model-name'\n\tassert model.usage.prompt_tokens == 20\n\tassert model.usage.completion_tokens == 30\n\tassert model.usage.total_tokens == 50\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tFileObject,\n\t],\n)\ndef test_response_file_list_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\" {\n        \"id\": \"12345\",\n        \"bytes\": 1024,\n        \"created_at\": 1617181723,\n        \"filename\": \"example.txt\",\n        \"object\": \"file\",\n        \"purpose\": \"example purpose\",\n        \"status\": \"uploaded\",\n        \"status_details\": \"File uploaded successfully\"\n      }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model, FileObject)\n\tassert model.id == '12345'\n\tassert model.bytes == 1024\n\tassert model.created_at == 1617181723\n\tassert model.filename == 'example.txt'\n\tassert model.object == 'file'\n\tassert model.purpose == 'example purpose'\n\tassert model.status == 'uploaded'\n\tassert model.status_details == 'File uploaded successfully'\n\n\n@pytest.mark.parametrize(\n\t'R',\n\t[\n\t\tListOfFileObject,\n\t],\n)\ndef test_response_file_list_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n    \"object\": \"list\",\n    \"data\": [\n      {\n        \"id\": \"12345\",\n        \"bytes\": 1024,\n        \"created_at\": 1617181723,\n        \"filename\": \"example.txt\",\n        \"object\": \"file\",\n        \"purpose\": \"example purpose\",\n        \"status\": \"uploaded\",\n        \"status_details\": \"File uploaded successfully\"\n      }\n    ],\n    \"has_more\": true\n  }\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model.data, list)\n\tassert isinstance(model.data[0], FileObject)\n\tassert model.data[0].id == '12345'\n\tassert model.data[0].bytes == 1024\n\tassert model.data[0].created_at == 1617181723\n\tassert model.data[0].filename == 'example.txt'\n\tassert model.data[0].object == 'file'\n\tassert model.data[0].purpose == 'example purpose'\n\tassert model.data[0].status == 'uploaded'\n\tassert model.data[0].status_details == 'File uploaded successfully'\n\tassert model.has_more == True\n\n\n@pytest.mark.parametrize('R', [ImagesResponded])\ndef test_response_image_model_cast(R: Type[BaseModel]) -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\tresponse = httpx.Response(\n\t\tstatus_code=200,\n\t\tcontent=\"\"\"{\n\"created\": 1234567890,\n\"data\": [\n  {\n    \"b64_json\": \"base64_encoded_string\",\n    \"url\": \"http://example.com/image.png\",\n    \"revised_prompt\": \"Revised prompt text\"\n  }\n]\n}\"\"\",\n\t)\n\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=response,\n\t\tcast_type=R,\n\t\tclient=MockClient(),\n\t\tstream=False,\n\t\tstream_cls=None,\n\t\toptions=opts,\n\t)\n\tmodel = http_response.parse()\n\n\tassert R == model.__class__\n\tassert isinstance(model.data, list)\n\tassert isinstance(model.data[0], GeneratedImage)\n\tassert model.data[0].b64_json == 'base64_encoded_string'\n"
  },
  {
    "path": "tests/unit_tests/sse_client/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit_tests/sse_client/test_stream.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Iterable, Type, cast\n\nimport httpx\n\nfrom zhipuai.core import HttpClient, StreamResponse, get_args\nfrom zhipuai.core._base_type import ResponseT\nfrom zhipuai.types.chat.chat_completion_chunk import ChatCompletionChunk\n\n\nclass MockClient:\n\t_strict_response_validation: bool = False\n\n\tdef _process_response_data(\n\t\tself,\n\t\t*,\n\t\tdata: object,\n\t\tcast_type: Type[ResponseT],\n\t\tresponse: httpx.Response,\n\t) -> ResponseT:\n\t\tpass\n\n\ndef test_stream_cls_chunk() -> None:\n\tMockClient._process_response_data = HttpClient._process_response_data\n\n\tdef body() -> Iterable[bytes]:\n\t\tyield b'data: {\"id\":\"8635243129834723621\",\"created\":1715329207,\"model\":\\\n            \"glm-4\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"1\"}}]}\\n\\n'\n\t\tyield b'data: {\"id\":\"8635243129834723621\",\"created\":1715329207,\"model\":\\\n            \"glm-4\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"2\"}}]}\\n\\n'\n\n\t_stream_cls = StreamResponse[ChatCompletionChunk]\n\thttp_response = httpx.Response(status_code=200, content=body())\n\n\tstream_cls = _stream_cls(\n\t\tcast_type=cast(type, get_args(_stream_cls)[0]),\n\t\tresponse=http_response,\n\t\tclient=MockClient(),\n\t)\n\tchat_completion_chunk1 = next(stream_cls)\n\n\tassert chat_completion_chunk1.choices[0].delta.content == '1'\n\tassert chat_completion_chunk1.choices[0].delta.role == 'assistant'\n\tassert chat_completion_chunk1.choices[0].index == 0\n\tassert chat_completion_chunk1.model == 'glm-4'\n\tchat_completion_chunk2 = next(stream_cls)\n\tassert chat_completion_chunk2.choices[0].delta.content == '2'\n\tassert chat_completion_chunk2.choices[0].delta.role == 'assistant'\n\tassert chat_completion_chunk2.choices[0].index == 0\n\tassert chat_completion_chunk2.model == 'glm-4'\n"
  },
  {
    "path": "tests/unit_tests/test_agents.py",
    "content": "def test_agents_completion_error_field():\n    from zhipuai.types.agents.agents_completion import AgentsCompletion, AgentsError, AgentsCompletionChoice, AgentsCompletionMessage, AgentsCompletionUsage\n\n    # 构造一个 AgentsError\n    error = AgentsError(code=\"404\", message=\"Not Found\")\n\n    # 构造一个完整的 AgentsCompletion\n    completion = AgentsCompletion(\n        agent_id=\"test_agent\",\n        conversation_id=\"conv_1\",\n        status=\"failed\",\n        choices=[\n            AgentsCompletionChoice(\n                index=0,\n                finish_reason=\"error\",\n                message=AgentsCompletionMessage(content=\"error\", role=\"system\")\n            )\n        ],\n        request_id=\"req_1\",\n        id=\"id_1\",\n        usage=AgentsCompletionUsage(prompt_tokens=1, completion_tokens=1, total_tokens=2),\n        error=error\n    )\n\n    # 检查 error 字段是否为 AgentsError 实例\n    assert isinstance(completion.error, AgentsError)\n    assert completion.error.code == \"404\"\n    assert completion.error.message == \"Not Found\"\n\n    # 检查序列化\n    as_dict = completion.model_dump()\n    assert as_dict[\"error\"][\"code\"] == \"404\"\n    assert as_dict[\"error\"][\"message\"] == \"Not Found\"\n    print(\"test_agents_completion_error_field passed.\")"
  },
  {
    "path": "tests/unit_tests/test_audio.py",
    "content": "def test_audio_error_field():\n    from zhipuai.types.audio.audio_speech_chunk import AudioSpeechChunk, AudioError, AudioSpeechChoice, AudioSpeechDelta\n\n    # 构造一个 AudioError\n    error = AudioError(code=\"500\", message=\"Internal Error\")\n\n    # 构造一个完整的 AudioSpeechChunk\n    chunk = AudioSpeechChunk(\n        choices=[\n            AudioSpeechChoice(\n                delta=AudioSpeechDelta(content=\"audio\", role=\"system\"),\n                finish_reason=\"error\",\n                index=0\n            )\n        ],\n        request_id=\"req_2\",\n        created=123456,\n        error=error\n    )\n\n    # 检查 error 字段是否为 AudioError 实例\n    assert isinstance(chunk.error, AudioError)\n    assert chunk.error.code == \"500\"\n    assert chunk.error.message == \"Internal Error\"\n\n    # 检查序列化\n    as_dict = chunk.model_dump()\n    assert as_dict[\"error\"][\"code\"] == \"500\"\n    assert as_dict[\"error\"][\"message\"] == \"Internal Error\"\n    print(\"test_audio_error_field passed.\")\n"
  },
  {
    "path": "tests/unit_tests/test_jwt.py",
    "content": "import jwt\nimport pytest\n\nfrom zhipuai.core._jwt_token import generate_token\n\n\ndef test_token() -> None:\n\t# 生成token\n\ttoken = generate_token('12345678.abcdefg')\n\tassert token is not None\n\n\t# 解析token\n\tpayload = jwt.decode(\n\t\ttoken,\n\t\t'abcdefg',\n\t\talgorithms='HS256',\n\t\toptions={'verify_signature': False},\n\t)\n\tassert payload is not None\n\tassert payload.get('api_key') == '12345678'\n\n\tapikey = 'invalid_api_key'\n\twith pytest.raises(Exception):\n\t\tgenerate_token(apikey)\n"
  },
  {
    "path": "tests/unit_tests/test_request_opt.py",
    "content": "import pytest\n\nfrom zhipuai.core._request_opt import FinalRequestOptions, NotGiven\n\n\n# Test Initialization and Default Values\ndef test_initialization():\n\tparams = FinalRequestOptions.construct(\n\t\tmethod='GET', url='http://example.com'\n\t)\n\tassert isinstance(params.max_retries, NotGiven)\n\tassert isinstance(params.timeout, NotGiven)\n\tassert isinstance(params.headers, NotGiven)\n\tassert params.json_data is None\n\n\n# Test get_max_retries Method\n@pytest.mark.parametrize(\n\t'max_retries_input, expected',\n\t[\n\t\t(NotGiven(), 5),  # Default case\n\t\t(3, 3),  # Specific number\n\t],\n)\ndef test_get_max_retries(max_retries_input, expected):\n\tparams = FinalRequestOptions.construct(\n\t\tmethod='GET', url='http://example.com', max_retries=max_retries_input\n\t)\n\tassert params.get_max_retries(5) == expected\n\n\n# Test construct Method\ndef test_construct():\n\tinput_data = {\n\t\t'max_retries': 3,\n\t\t'timeout': 10.0,\n\t\t'headers': {'Content-Type': 'application/json'},\n\t}\n\tparams = FinalRequestOptions.construct(**input_data)\n\tassert params.max_retries == input_data['max_retries']\n\tassert params.timeout == input_data['timeout']\n\tassert params.headers == input_data['headers']\n"
  },
  {
    "path": "tests/unit_tests/test_response.py",
    "content": "import httpx\nfrom httpx import URL, ByteStream, Headers, Request, Response\nfrom typing_extensions import Dict, Type\n\nfrom zhipuai.core import StreamResponse\nfrom zhipuai.core._base_type import ResponseT\nfrom zhipuai.core._http_client import HttpClient\nfrom zhipuai.core._request_opt import FinalRequestOptions\nfrom zhipuai.core._response import APIResponse\n\n\n# Mock objects for HttpClient and StreamResponse if necessary\nclass MockHttpClient:\n\t_strict_response_validation: bool = False\n\n\t# Implement necessary mock methods or attributes\n\tdef _process_response_data(\n\t\tself,\n\t\t*,\n\t\tdata: object,\n\t\tcast_type: Type[ResponseT],\n\t\tresponse: httpx.Response,\n\t) -> ResponseT:\n\t\treturn data\n\n\nclass MockStreamResponse(StreamResponse[ResponseT]):\n\t# Implement necessary mock methods or attributes\n\tdef __init__(\n\t\tself,\n\t\t*,\n\t\tcast_type: Type[ResponseT],\n\t\tresponse: httpx.Response,\n\t\tclient: HttpClient,\n\t) -> None:\n\t\tsuper().__init__(cast_type=cast_type, response=response, client=client)\n\t\tself.response = response\n\t\tself._cast_type = cast_type\n\t\t# self._data_process_func = client._process_response_data\n\t\t# self._strem_chunks = self.__stream__()\n\n\tdef __iter__(self):\n\t\tfor item in self.response.iter_lines():\n\t\t\tyield item\n\n\n# Test Initialization\ndef test_http_response_initialization():\n\traw_response = Response(200)\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\thttp_response = APIResponse(\n\t\traw=raw_response,\n\t\tcast_type=str,\n\t\tclient=MockHttpClient(),\n\t\tstream=False,\n\t\toptions=opts,\n\t)\n\tassert http_response.http_response == raw_response\n\n\n# Test parse Method\ndef test_parse_method():\n\traw_response = Response(\n\t\t200,\n\t\theaders=Headers({'content-type': 'application/json'}),\n\t\tcontent=b'{\"key\": \"value\"}',\n\t)\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\n\thttp_response = APIResponse(\n\t\traw=raw_response,\n\t\tcast_type=Dict[str, object],\n\t\tclient=MockHttpClient(),\n\t\tstream=False,\n\t\toptions=opts,\n\t)\n\tparsed_data = http_response.parse()\n\tassert parsed_data == {'key': 'value'}\n\thttp_response = APIResponse(\n\t\traw=raw_response,\n\t\tcast_type=str,\n\t\tclient=MockHttpClient(),\n\t\tstream=False,\n\t\toptions=opts,\n\t)\n\tparsed_data = http_response.parse()\n\tassert parsed_data == '{\"key\": \"value\"}'\n\n\traw_response = Response(\n\t\t200,\n\t\tcontent=b'{\"key\": \"value\"}',\n\t\tstream=ByteStream(b'{\"key\": \"value\"}\\n\"foo\"\\n\"boo\"\\n'),\n\t)\n\thttp_response = APIResponse(\n\t\traw=raw_response,\n\t\tcast_type=str,\n\t\tclient=MockHttpClient(),\n\t\tstream=True,\n\t\toptions=opts,\n\t\tstream_cls=MockStreamResponse[str],\n\t)\n\tparsed_data = http_response.parse()\n\texcepted_data = ['{\"key\": \"value\"}', '\"foo\"', '\"boo\"']\n\tdata = [chunk.strip() for chunk in parsed_data]\n\tassert data == excepted_data\n\n\n# Test properties\ndef test_properties():\n\topts = FinalRequestOptions.construct(method='get', url='path')\n\theaders = Headers({'content-type': 'application/json'})\n\trequest = Request(method='GET', url='http://example.com')\n\traw_response = Response(200, headers=headers, request=request)\n\thttp_response = APIResponse(\n\t\traw=raw_response,\n\t\tcast_type=str,\n\t\tclient=MockHttpClient(),\n\t\tstream=False,\n\t\toptions=opts,\n\t)\n\n\tassert http_response.headers == headers\n\tassert http_response.http_request == request\n\tassert http_response.status_code == 200\n\tassert http_response.url == URL('http://example.com')\n\tassert http_response.method == 'GET'\n"
  },
  {
    "path": "tests/unit_tests/test_sdk_import.py",
    "content": "def test_sdk_import_unit():\n\timport zhipuai\n\n\tprint(zhipuai.__version__)\n\n\ndef test_os_import_unit():\n\timport os\n\n\tprint(os)\n\n\ndef test_sdk_import():\n\tfrom zhipuai import ZhipuAI\n\n\tclient = ZhipuAI(api_key='empty')  # 请填写您自己的APIKey\n\n\tif client is not None:\n\t\tprint('SDK导入成功')\n\telse:\n\t\tprint('SDK导入失败')\n"
  },
  {
    "path": "tests/unit_tests/test_streaming.py",
    "content": "from typing import Iterator\n\nimport pytest\n\nfrom zhipuai.core._sse_client import SSELineParser\n\n\ndef test_basic() -> None:\n\tdef body() -> Iterator[str]:\n\t\tyield 'event: completion'\n\t\tyield 'data: {\"foo\":true}'\n\t\tyield ''\n\n\tit = SSELineParser().iter_lines(body())\n\tsse = next(it)\n\tassert sse.event == 'completion'\n\tassert sse.json_data() == {'foo': True}\n\n\twith pytest.raises(StopIteration):\n\t\tnext(it)\n\n\ndef test_data_missing_event() -> None:\n\tdef body() -> Iterator[str]:\n\t\tyield 'data: {\"foo\":true}'\n\t\tyield ''\n\n\tit = SSELineParser().iter_lines(body())\n\tsse = next(it)\n\tassert sse.event is None\n\tassert sse.json_data() == {'foo': True}\n\n\twith pytest.raises(StopIteration):\n\t\tnext(it)\n\n\ndef test_event_missing_data() -> None:\n\tdef body() -> Iterator[str]:\n\t\tyield 'event: ping'\n\t\tyield ''\n\n\tit = SSELineParser().iter_lines(body())\n\tsse = next(it)\n\tassert sse.event == 'ping'\n\tassert sse.data == ''\n\n\twith pytest.raises(StopIteration):\n\t\tnext(it)\n\n\ndef test_multiple_events() -> None:\n\tdef body() -> Iterator[str]:\n\t\tyield 'event: ping'\n\t\tyield ''\n\t\tyield 'event: completion'\n\t\tyield ''\n\n\tit = SSELineParser().iter_lines(body())\n\n\tsse = next(it)\n\tassert sse.event == 'ping'\n\tassert sse.data == ''\n\n\tsse = next(it)\n\tassert sse.event == 'completion'\n\tassert sse.data == ''\n\n\twith pytest.raises(StopIteration):\n\t\tnext(it)\n\n\ndef test_multiple_events_with_data() -> None:\n\tdef body() -> Iterator[str]:\n\t\tyield 'event: ping'\n\t\tyield 'data: {\"foo\":true}'\n\t\tyield ''\n\t\tyield 'event: completion'\n\t\tyield 'data: {\"bar\":false}'\n\t\tyield ''\n\n\tit = SSELineParser().iter_lines(body())\n\n\tsse = next(it)\n\tassert sse.event == 'ping'\n\tassert sse.json_data() == {'foo': True}\n\n\tsse = next(it)\n\tassert sse.event == 'completion'\n\tassert sse.json_data() == {'bar': False}\n\n\twith pytest.raises(StopIteration):\n\t\tnext(it)\n"
  },
  {
    "path": "zhipuai/__init__.py",
    "content": "\nfrom ._client import ZhipuAI\n\nfrom .core import (\n    ZhipuAIError,\n    APIStatusError,\n    APIRequestFailedError,\n    APIAuthenticationError,\n    APIReachLimitError,\n    APIInternalError,\n    APIServerFlowExceedError,\n    APIResponseError,\n    APIResponseValidationError,\n    APIConnectionError,\n    APITimeoutError,\n)\n\nfrom .__version__ import __version__\n"
  },
  {
    "path": "zhipuai/__version__.py",
    "content": "__version__ = 'v2.1.5.20250725'"
  },
  {
    "path": "zhipuai/_client.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, Mapping\n\nfrom typing_extensions import override\n\nfrom .core import _jwt_token\nfrom .core import ZhipuAIError\nfrom .core import HttpClient, ZHIPUAI_DEFAULT_MAX_RETRIES\nfrom .core import NotGiven, NOT_GIVEN\nfrom . import api_resource\nimport os\nimport httpx\nfrom httpx import Timeout\n\n\nclass ZhipuAI(HttpClient):\n    chat: api_resource.chat.Chat\n    api_key: str\n    _disable_token_cache: bool = True\n    source_channel: str\n\n    def __init__(\n            self,\n            *,\n            api_key: str | None = None,\n            base_url: str | httpx.URL | None = None,\n            timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,\n            max_retries: int = ZHIPUAI_DEFAULT_MAX_RETRIES,\n            http_client: httpx.Client | None = None,\n            custom_headers: Mapping[str, str] | None = None,\n            disable_token_cache: bool = True,\n            _strict_response_validation: bool = False,\n            source_channel: str | None = None\n    ) -> None:\n        if api_key is None:\n            api_key = os.environ.get(\"ZHIPUAI_API_KEY\")\n        if api_key is None:\n            raise ZhipuAIError(\"未提供api_key，请通过参数或环境变量提供\")\n        self.api_key = api_key\n        self.source_channel = source_channel\n        self._disable_token_cache = disable_token_cache\n\n        if base_url is None:\n            base_url = os.environ.get(\"ZHIPUAI_BASE_URL\")\n        if base_url is None:\n            base_url = f\"https://open.bigmodel.cn/api/paas/v4\"\n        from .__version__ import __version__\n        super().__init__(\n            version=__version__,\n            base_url=base_url,\n            max_retries=max_retries,\n            timeout=timeout,\n            custom_httpx_client=http_client,\n            custom_headers=custom_headers,\n            _strict_response_validation=_strict_response_validation,\n        )\n        self.chat = api_resource.chat.Chat(self)\n        self.images = api_resource.images.Images(self)\n        self.embeddings = api_resource.embeddings.Embeddings(self)\n        self.files = api_resource.files.Files(self)\n        self.fine_tuning = api_resource.fine_tuning.FineTuning(self)\n        self.batches = api_resource.Batches(self)\n        self.knowledge = api_resource.Knowledge(self)\n        self.tools = api_resource.Tools(self)\n        self.videos = api_resource.Videos(self)\n        self.assistant = api_resource.Assistant(self)\n        self.web_search = api_resource.WebSearchApi(self)\n        self.audio = api_resource.audio.Audio(self)\n        self.moderations = api_resource.moderation.Moderations(self)\n        self.agents = api_resource.agents.Agents(self)\n        self.file_parser = api_resource.file_parser.FileParser(self)\n\n    @property\n    @override\n    def auth_headers(self) -> dict[str, str]:\n        api_key = self.api_key\n        source_channel = self.source_channel or \"python-sdk\"\n        if self._disable_token_cache:\n            return {\"Authorization\": f\"Bearer {api_key}\",\"x-source-channel\": source_channel}\n        else:\n            return {\"Authorization\": f\"Bearer {_jwt_token.generate_token(api_key)}\",\"x-source-channel\": source_channel}\n\n    def __del__(self) -> None:\n        if (not hasattr(self, \"_has_custom_http_client\")\n                or not hasattr(self, \"close\")\n                or not hasattr(self, \"_client\")):\n            # if the '__init__' method raised an error, self would not have client attr\n            return\n\n        if self._has_custom_http_client:\n            return\n\n        self.close()\n"
  },
  {
    "path": "zhipuai/api_resource/__init__.py",
    "content": "from .chat import (\n    AsyncCompletions,\n    Chat,\n    Completions,\n)\nfrom .images import (\n    Images\n)\nfrom .embeddings import (\n    Embeddings\n)\nfrom .files import (\n    Files,\n    FilesWithRawResponse\n)\nfrom .fine_tuning import (\n    FineTuning\n)\n\nfrom .batches import (\n    Batches\n)\n\nfrom .knowledge import (\n    Knowledge\n)\nfrom .tools import (\n    Tools\n)\nfrom .videos import (\n    Videos,\n)\nfrom .assistant import (\n    Assistant,\n)\nfrom .audio import (\n    Audio\n)\n\nfrom .moderation import (\n    Moderations\n)\n\nfrom .web_search import (\n    WebSearchApi\n)\n\nfrom .agents import (\n    Agents\n)\n\nfrom .audio import (\n    Audio\n)\n\nfrom .file_parser import (\n    FileParser\n)\n\n__all__ = [\n    'Videos',\n    'AsyncCompletions',\n    'Chat',\n    'Completions',\n    'Images',\n    'Embeddings',\n    'Files',\n    'FilesWithRawResponse',\n    'FineTuning',\n    'Batches',\n    'Knowledge',\n    'Tools',\n    'Assistant',\n    'Audio',\n    'Moderation',\n    'FileParser'\n]\n"
  },
  {
    "path": "zhipuai/api_resource/agents/__init__.py",
    "content": "from zhipuai.api_resource.agents.agents import Agents\n\n__all__= [\n    \"Agents\"\n]\n"
  },
  {
    "path": "zhipuai/api_resource/agents/agents.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, Optional, TYPE_CHECKING, Dict\n\nimport httpx\nimport logging\nfrom typing_extensions import Literal\n\nfrom ...core import BaseAPI, deepcopy_minimal\nfrom ...core import NotGiven, NOT_GIVEN, Headers, Query, Body\nfrom ...core import make_request_options\nfrom ...core import StreamResponse\nfrom ...types.agents.agents_completion import AgentsCompletion\nfrom ...types.agents.agents_completion_chunk import AgentsCompletionChunk\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\nclass Agents(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def invoke(\n            self,\n            agent_id: Optional[str] | NotGiven = NOT_GIVEN,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            messages: Union[str, List[str], List[int], object, None] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            custom_variables: object = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> AgentsCompletion | StreamResponse[AgentsCompletionChunk]:\n        body = deepcopy_minimal({\n            \"agent_id\": agent_id,\n            \"request_id\": request_id,\n            \"user_id\": user_id,\n            \"messages\": messages,\n            \"sensitive_word_check\": sensitive_word_check,\n            \"stream\": stream,\n            \"custom_variables\": custom_variables\n        })\n\n        return self._post(\n            \"/v1/agents\",\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=AgentsCompletion,\n            stream=stream or False,\n            stream_cls=StreamResponse[AgentsCompletionChunk],\n        )\n\n    def async_result(\n            self,\n            agent_id: Optional[str] | NotGiven = NOT_GIVEN,\n            async_id: Optional[str] | NotGiven = NOT_GIVEN,\n            conversation_id: Optional[str] | NotGiven = NOT_GIVEN,\n            custom_variables: object = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> AgentsCompletion:\n        body = deepcopy_minimal({\n            \"agent_id\": agent_id,\n            \"async_id\": async_id,\n            \"conversation_id\": conversation_id,\n            \"custom_variables\": custom_variables\n        })\n        return self._post(\n            \"/v1/agents/async-result\",\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=AgentsCompletion,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/assistant/__init__.py",
    "content": "\n\nfrom zhipuai.api_resource.assistant.assistant import Assistant\n\n__all__= [\n    \"Assistant\"\n]\n"
  },
  {
    "path": "zhipuai/api_resource/assistant/assistant.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional, Dict\nfrom typing_extensions import Literal\n\nfrom ...types.assistant import AssistantCompletion\nfrom ...types.assistant.assistant_conversation_resp import ConversationUsageList, ConversationUsageListResp\nfrom ...types.assistant.assistant_support_resp import AssistantSupportResp\nfrom ...core import BaseAPI, maybe_transform, StreamResponse\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven\n\nimport httpx\n\nfrom ...core import (\n    make_request_options,\n)\nfrom ...core import deepcopy_minimal, extract_files\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\nfrom ...types.assistant import assistant_create_params\nfrom ...types.assistant import assistant_conversation_params\n\n__all__ = [\"Assistant\"]\n\n\nclass Assistant(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def conversation(\n            self,\n            assistant_id: str,\n            messages: List[assistant_create_params.ConversationMessage],\n            model: str = None,\n            *,\n            stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            conversation_id: Optional[str] = None,\n            attachments: Optional[List[assistant_create_params.AssistantAttachments]] = None,\n            metadata: dict | None = None,\n            request_id: str = None,\n            user_id: str = None,\n            extra_parameters: Optional[assistant_create_params.ExtraParameters] = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> AssistantCompletion | StreamResponse[AssistantCompletion]:\n        body = deepcopy_minimal(\n            {\n                \"assistant_id\": assistant_id,\n                \"messages\": messages,\n                \"stream\": stream,\n                \"conversation_id\": conversation_id,\n                \"attachments\": attachments,\n                \"metadata\": metadata,\n                \"request_id\": request_id,\n                \"user_id\": user_id,\n                \"extra_parameters\": extra_parameters\n            }\n        )\n        return self._post(\n            \"/assistant\",\n            body=maybe_transform(body, assistant_create_params.AssistantParameters),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=AssistantCompletion,\n            stream=stream or False,\n            stream_cls=StreamResponse[AssistantCompletion],\n        )\n\n    def query_support(\n            self,\n            *,\n            assistant_id_list: List[str] = None,\n            request_id: str = None,\n            user_id: str = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> AssistantSupportResp:\n        body = deepcopy_minimal(\n            {\n                \"assistant_id_list\": assistant_id_list,\n                \"request_id\": request_id,\n                \"user_id\": user_id,\n            }\n        )\n        return self._post(\n            \"/assistant/list\",\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=AssistantSupportResp,\n        )\n\n    def query_conversation_usage(\n            self,\n            assistant_id: str,\n            page: int = 1,\n            page_size: int = 10,\n            *,\n            request_id: str = None,\n            user_id: str = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> ConversationUsageListResp:\n        body = deepcopy_minimal(\n            {\n                \"assistant_id\": assistant_id,\n                \"page\": page,\n                \"page_size\": page_size,\n                \"request_id\": request_id,\n                \"user_id\": user_id,\n            }\n        )\n        return self._post(\n            \"/assistant/conversation/list\",\n\n            body=maybe_transform(body, assistant_conversation_params.ConversationParameters),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=ConversationUsageListResp,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/audio/__init__.py",
    "content": "from .audio import (\n    Audio\n)\n\nfrom .transcriptions import (\n    Transcriptions\n)\n\n\n__all__ = [\n    'Audio',\n    'Transcriptions'\n]"
  },
  {
    "path": "zhipuai/api_resource/audio/audio.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional, Dict\nfrom .transcriptions import Transcriptions\n\nfrom zhipuai.core._utils import extract_files\n\nfrom zhipuai.types.sensitive_word_check import SensitiveWordCheckRequest\nfrom zhipuai.types.audio import AudioSpeechParams\nfrom ...types.audio import audio_customization_param\n\nfrom zhipuai.core import BaseAPI, maybe_transform, StreamResponse\nfrom zhipuai.core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\nfrom zhipuai.core import _legacy_response\n\nimport httpx\nfrom ...core import BaseAPI, cached_property\n\nfrom zhipuai.core import (\n    make_request_options,\n)\nfrom zhipuai.core import deepcopy_minimal\nfrom ...types.audio.audio_speech_chunk import AudioSpeechChunk\n\nif TYPE_CHECKING:\n    from zhipuai._client import ZhipuAI\n\n__all__ = [\"Audio\"]\n\n\nclass Audio(BaseAPI):\n\n    @cached_property\n    def transcriptions(self) -> Transcriptions:\n        return Transcriptions(self._client)\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def speech(\n            self,\n            *,\n            model: str,\n            input: str = None,\n            voice: str = None,\n            response_format: str = None,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            request_id: str = None,\n            user_id: str = None,\n            stream: bool = False,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n            encode_format: str,\n    ) -> _legacy_response.HttpxBinaryResponseContent | StreamResponse[AudioSpeechChunk]:\n        body = deepcopy_minimal(\n            {\n                \"model\": model,\n                \"input\": input,\n                \"voice\": voice,\n                \"stream\": stream,\n                \"response_format\": response_format,\n                \"sensitive_word_check\": sensitive_word_check,\n                \"request_id\": request_id,\n                \"user_id\": user_id,\n                \"encode_format\": encode_format\n            }\n        )\n        return self._post(\n            \"/audio/speech\",\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_legacy_response.HttpxBinaryResponseContent,\n            stream= stream or False,\n            stream_cls=StreamResponse[AudioSpeechChunk]\n        )\n\n    def customization(\n            self,\n            *,\n            model: str,\n            input: str = None,\n            voice_text: str = None,\n            voice_data: FileTypes = None,\n            response_format: str = None,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            request_id: str = None,\n            user_id: str = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> _legacy_response.HttpxBinaryResponseContent:\n        body = deepcopy_minimal(\n            {\n                \"model\": model,\n                \"input\": input,\n                \"voice_text\": voice_text,\n                \"voice_data\": voice_data,\n                \"response_format\": response_format,\n                \"sensitive_word_check\": sensitive_word_check,\n                \"request_id\": request_id,\n                \"user_id\": user_id\n            }\n        )\n        files = extract_files(cast(Mapping[str, object], body), paths=[[\"voice_data\"]])\n\n        if files:\n            extra_headers = {\"Content-Type\": \"multipart/form-data\", **(extra_headers or {})}\n        return self._post(\n            \"/audio/customization\",\n            body=maybe_transform(body, audio_customization_param.AudioCustomizationParam),\n            files=files,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_legacy_response.HttpxBinaryResponseContent\n        )"
  },
  {
    "path": "zhipuai/api_resource/audio/transcriptions.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional, Dict\nfrom ...types.audio import transcriptions_create_param\n\nimport httpx\nimport logging\nfrom typing_extensions import Literal\n\nfrom ...core import BaseAPI, deepcopy_minimal, maybe_transform, drop_prefix_image_data\nfrom ...core import make_request_options\nfrom ...core import StreamResponse\nfrom ...types.chat.chat_completion import Completion\nfrom ...types.chat.chat_completion_chunk import ChatCompletionChunk\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\nfrom zhipuai.core._utils import extract_files\n\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\n__all__ = [\"Transcriptions\"]\n\nclass Transcriptions(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            file: FileTypes,\n            model: str,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            temperature: Optional[float] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN\n    ) -> Completion | StreamResponse[ChatCompletionChunk]:\n        if temperature is not None and temperature != NOT_GIVEN:\n            if temperature <= 0:\n                temperature = 0.01\n            if temperature >= 1:\n                temperature = 0.99\n\n        body = deepcopy_minimal({\n            \"model\": model,\n            \"file\": file,\n            \"request_id\": request_id,\n            \"user_id\": user_id,\n            \"temperature\": temperature,\n            \"sensitive_word_check\": sensitive_word_check,\n            \"stream\": stream\n        })\n        files = extract_files(cast(Mapping[str, object], body), paths=[[\"file\"]])\n        if files:\n            extra_headers = {\"Content-Type\": \"multipart/form-data\", **(extra_headers or {})}\n        return self._post(\n            \"/audio/transcriptions\",\n            body=maybe_transform(body, transcriptions_create_param.TranscriptionsParam),\n            files=files,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=Completion,\n            stream=stream or False,\n            stream_cls=StreamResponse[ChatCompletionChunk],\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/batches.py",
    "content": "from __future__ import annotations\n\nimport httpx\nfrom typing import TYPE_CHECKING, Literal, Optional, Dict\n\nfrom ..core import BaseAPI, NotGiven, NOT_GIVEN, Headers, Body, maybe_transform, make_request_options\nfrom ..core.pagination import SyncCursorPage\nfrom ..types import batch_create_params, batch_list_params\nfrom ..types.batch import Batch\n\nif TYPE_CHECKING:\n    from .._client import ZhipuAI\n\n\nclass Batches(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            completion_window: str | None = None,\n            endpoint: Literal[\"/v1/chat/completions\", \"/v1/embeddings\"],\n            input_file_id: str,\n            metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,\n            auto_delete_input_file: bool = True,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> Batch:\n        return self._post(\n            \"/batches\",\n            body=maybe_transform(\n                {\n                    \"completion_window\": completion_window,\n                    \"endpoint\": endpoint,\n                    \"input_file_id\": input_file_id,\n                    \"metadata\": metadata,\n                    \"auto_delete_input_file\":auto_delete_input_file\n                },\n                batch_create_params.BatchCreateParams,\n            ),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=Batch,\n        )\n\n\n    def retrieve(\n            self,\n            batch_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> Batch:\n        \"\"\"\n        Retrieves a batch.\n\n        Args:\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not batch_id:\n            raise ValueError(f\"Expected a non-empty value for `batch_id` but received {batch_id!r}\")\n        return self._get(\n            f\"/batches/{batch_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=Batch,\n        )\n\n    def list(\n            self,\n            *,\n            after: str | NotGiven = NOT_GIVEN,\n            limit: int | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> SyncCursorPage[Batch]:\n        \"\"\"List your organization's batches.\n\n        Args:\n          after: A cursor for use in pagination.\n\n            `after` is an object ID that defines your place\n              in the list. For instance, if you make a list request and receive 100 objects,\n              ending with obj_foo, your subsequent call can include after=obj_foo in order to\n              fetch the next page of the list.\n\n          limit: A limit on the number of objects to be returned. Limit can range between 1 and\n              100, and the default is 20.\n\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        return self._get_api_list(\n            \"/batches\",\n            page=SyncCursorPage[Batch],\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query=maybe_transform(\n                    {\n                        \"after\": after,\n                        \"limit\": limit,\n                    },\n                    batch_list_params.BatchListParams,\n                ),\n            ),\n            model=Batch,\n        )\n\n    def cancel(\n            self,\n            batch_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> Batch:\n        \"\"\"\n        Cancels an in-progress batch.\n\n        Args:\n          batch_id: The ID of the batch to cancel.\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n\n        \"\"\"\n        if not batch_id:\n            raise ValueError(f\"Expected a non-empty value for `batch_id` but received {batch_id!r}\")\n        return self._post(\n            f\"/batches/{batch_id}/cancel\",\n            options=make_request_options(\n                extra_headers=extra_headers,extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=Batch,\n        )\n\n"
  },
  {
    "path": "zhipuai/api_resource/chat/__init__.py",
    "content": "from .async_completions import (\n    AsyncCompletions\n)\n\nfrom .chat import (\n    Chat\n)\n\nfrom .completions import (\n    Completions\n)\n\n__all__ = [\n    'AsyncCompletions'\n    'Chat'\n    'Completions'\n]"
  },
  {
    "path": "zhipuai/api_resource/chat/async_completions.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, Optional, TYPE_CHECKING, Dict\n\nimport httpx\nimport logging\nfrom typing_extensions import Literal\n\nfrom ...core import BaseAPI, maybe_transform, drop_prefix_image_data\nfrom ...core import NotGiven, NOT_GIVEN, Headers, Body\nfrom ...core import make_request_options\nfrom ...types.chat.async_chat_completion import AsyncTaskStatus, AsyncCompletion\nfrom ...types.chat.code_geex import code_geex_params\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\nclass AsyncCompletions(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            model: str,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            temperature: Optional[float] | NotGiven = NOT_GIVEN,\n            top_p: Optional[float] | NotGiven = NOT_GIVEN,\n            max_tokens: int | NotGiven = NOT_GIVEN,\n            seed: int | NotGiven = NOT_GIVEN,\n            messages: Union[str, List[str], List[int], List[List[int]], None],\n            stop: Optional[Union[str, List[str], None]] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            tools: Optional[object] | NotGiven = NOT_GIVEN,\n            tool_choice: str | NotGiven = NOT_GIVEN,\n            meta: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,\n            extra: Optional[code_geex_params.CodeGeexExtra] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n            response_format: object | None = None,\n            thinking: object | None = None,\n    ) -> AsyncTaskStatus:\n        _cast_type = AsyncTaskStatus\n        logger.debug(f\"temperature:{temperature}, top_p:{top_p}\")\n        if temperature is not None and temperature != NOT_GIVEN:\n\n            if temperature <= 0:\n                do_sample = False\n                temperature = 0.01\n                # logger.warning(\"temperature:取值范围是：(0.0, 1.0) 开区间，do_sample重写为:false（参数top_p temperture不生效）\")\n            if temperature >= 1:\n                temperature = 0.99\n                # logger.warning(\"temperature:取值范围是：(0.0, 1.0) 开区间\")\n        if top_p is not None and top_p != NOT_GIVEN:\n\n            if top_p >= 1:\n                top_p = 0.99\n                # logger.warning(\"top_p:取值范围是：(0.0, 1.0) 开区间，不能等于 0 或 1\")\n            if top_p <= 0:\n                top_p = 0.01\n                # logger.warning(\"top_p:取值范围是：(0.0, 1.0) 开区间，不能等于 0 或 1\")\n\n        logger.debug(f\"temperature:{temperature}, top_p:{top_p}\")\n        if isinstance(messages, List):\n            for item in messages:\n                if item.get('content'):\n                    item['content'] = drop_prefix_image_data(item['content'])\n\n        body = {\n            \"model\": model,\n            \"request_id\": request_id,\n            \"user_id\": user_id,\n            \"temperature\": temperature,\n            \"top_p\": top_p,\n            \"do_sample\": do_sample,\n            \"max_tokens\": max_tokens,\n            \"seed\": seed,\n            \"messages\": messages,\n            \"stop\": stop,\n            \"sensitive_word_check\": sensitive_word_check,\n            \"tools\": tools,\n            \"tool_choice\": tool_choice,\n            \"meta\": meta,\n            \"extra\": maybe_transform(extra, code_geex_params.CodeGeexExtra),\n            \"response_format\": response_format,\n            \"thinking\": thinking\n        }\n        return self._post(\n            \"/async/chat/completions\",\n\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_cast_type,\n            stream=False,\n        )\n\n    def retrieve_completion_result(\n            self,\n            id: str,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> Union[AsyncCompletion, AsyncTaskStatus]:\n        _cast_type = Union[AsyncCompletion, AsyncTaskStatus]\n        return self._get(\n            path=f\"/async-result/{id}\",\n            cast_type=_cast_type,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/chat/chat.py",
    "content": "from typing import TYPE_CHECKING\nfrom .completions import Completions\nfrom .async_completions import AsyncCompletions\nfrom ...core import BaseAPI, cached_property\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\nclass Chat(BaseAPI):\n\n    @cached_property\n    def completions(self) -> Completions:\n        return Completions(self._client)\n\n    @cached_property\n    def asyncCompletions(self) -> AsyncCompletions:\n        return AsyncCompletions(self._client)"
  },
  {
    "path": "zhipuai/api_resource/chat/completions.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, Optional, TYPE_CHECKING, Dict\n\nimport httpx\nimport logging\nfrom typing_extensions import Literal\n\nfrom ...core import BaseAPI, deepcopy_minimal, maybe_transform, drop_prefix_image_data\nfrom ...core import NotGiven, NOT_GIVEN, Headers, Query, Body\nfrom ...core import make_request_options\nfrom ...core import StreamResponse\nfrom ...types.chat.chat_completion import Completion\nfrom ...types.chat.chat_completion_chunk import ChatCompletionChunk\nfrom ...types.chat.code_geex import code_geex_params\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\nclass Completions(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            model: str,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            temperature: Optional[float] | NotGiven = NOT_GIVEN,\n            top_p: Optional[float] | NotGiven = NOT_GIVEN,\n            max_tokens: int | NotGiven = NOT_GIVEN,\n            seed: int | NotGiven = NOT_GIVEN,\n            messages: Union[str, List[str], List[int], object, None],\n            stop: Optional[Union[str, List[str], None]] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            tools: Optional[object] | NotGiven = NOT_GIVEN,\n            tool_choice: str | NotGiven = NOT_GIVEN,\n            meta: Optional[Dict[str,str]] | NotGiven = NOT_GIVEN,\n            extra: Optional[code_geex_params.CodeGeexExtra] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n            response_format : object | None = None,\n            thinking :  object | None = None,\n    ) -> Completion | StreamResponse[ChatCompletionChunk]:\n        logger.debug(f\"temperature:{temperature}, top_p:{top_p}\")\n        if temperature is not None and temperature != NOT_GIVEN:\n\n            if temperature <= 0:\n                do_sample = False\n                temperature = 0.01\n                # logger.warning(\"temperature:取值范围是：(0.0, 1.0) 开区间，do_sample重写为:false（参数top_p temperture不生效）\")\n            if temperature >= 1:\n                temperature = 0.99\n                # logger.warning(\"temperature:取值范围是：(0.0, 1.0) 开区间\")\n        if top_p is not None and top_p != NOT_GIVEN:\n\n            if top_p >= 1:\n                top_p = 0.99\n                # logger.warning(\"top_p:取值范围是：(0.0, 1.0) 开区间，不能等于 0 或 1\")\n            if top_p <= 0:\n                top_p = 0.01\n                # logger.warning(\"top_p:取值范围是：(0.0, 1.0) 开区间，不能等于 0 或 1\")\n\n        logger.debug(f\"temperature:{temperature}, top_p:{top_p}\")\n        if isinstance(messages, List):\n            for item in messages:\n                if item.get('content'):\n                    item['content'] = drop_prefix_image_data(item['content'])\n\n        body = deepcopy_minimal({\n            \"model\": model,\n            \"request_id\": request_id,\n            \"user_id\": user_id,\n            \"temperature\": temperature,\n            \"top_p\": top_p,\n            \"do_sample\": do_sample,\n            \"max_tokens\": max_tokens,\n            \"seed\": seed,\n            \"messages\": messages,\n            \"stop\": stop,\n            \"sensitive_word_check\": sensitive_word_check,\n            \"stream\": stream,\n            \"tools\": tools,\n            \"tool_choice\": tool_choice,\n            \"meta\": meta,\n            \"extra\": maybe_transform(extra, code_geex_params.CodeGeexExtra),\n            \"response_format\": response_format,\n            \"thinking\": thinking\n        })\n        return self._post(\n            \"/chat/completions\",\n            body=body,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=Completion,\n            stream=stream or False,\n            stream_cls=StreamResponse[ChatCompletionChunk],\n        )\n\n\n"
  },
  {
    "path": "zhipuai/api_resource/embeddings.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, Optional, TYPE_CHECKING\n\nimport httpx\n\nfrom ..core import BaseAPI, Body\nfrom ..core import NotGiven, NOT_GIVEN, Headers\nfrom ..core import make_request_options\nfrom ..types.embeddings import EmbeddingsResponded\n\nif TYPE_CHECKING:\n    from .._client import ZhipuAI\n\n\nclass Embeddings(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            input: Union[str, List[str], List[int], List[List[int]]],\n            model: Union[str],\n            dimensions: Union[int]| NotGiven = NOT_GIVEN,\n            encoding_format: str | NotGiven = NOT_GIVEN,\n            user: str | NotGiven = NOT_GIVEN,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[object] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            disable_strict_validation: Optional[bool] | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> EmbeddingsResponded:\n        _cast_type = EmbeddingsResponded\n        if disable_strict_validation:\n            _cast_type = object\n        return self._post(\n            \"/embeddings\",\n            body={\n                \"input\": input,\n                \"model\": model,\n                \"dimensions\": dimensions,\n                \"encoding_format\": encoding_format,\n                \"user\": user,\n                \"request_id\": request_id,\n                \"sensitive_word_check\": sensitive_word_check,\n            },\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_cast_type,\n            stream=False,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/file_parser/__init__.py",
    "content": "from .file_parser import FileParser\n\n__all__ = ['FileParser']"
  },
  {
    "path": "zhipuai/api_resource/file_parser/file_parser.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Mapping, cast\n\nimport httpx\nfrom typing_extensions import Literal\n\nfrom ...core import BaseAPI, maybe_transform\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\nfrom ...core import _legacy_binary_response\nfrom ...core import _legacy_response\nfrom ...core import deepcopy_minimal, extract_files\nfrom ...core import (\n    make_request_options,\n)\nfrom ...types.file_parser.file_parser_create_params import FileParserCreateParams\nfrom ...types.file_parser.file_parser_resp import FileParserTaskCreateResp\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"FileParser\"]\n\n\nclass FileParser(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            file: FileTypes = None,\n            file_type: str = None,\n            tool_type: Literal[\"simple\", \"doc2x\", \"tencent\", \"zhipu-pro\"],\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FileParserTaskCreateResp:\n\n        if not file:\n            raise ValueError(\"At least one `file` must be provided.\")\n        body = deepcopy_minimal(\n            {\n                \"file\": file,\n                \"file_type\": file_type,\n                \"tool_type\": tool_type,\n            }\n        )\n\n        files = extract_files(cast(Mapping[str, object], body), paths=[[\"file\"]])\n        if files:\n            # It should be noted that the actual Content-Type header that will be\n            # sent to the server will contain a `boundary` parameter, e.g.\n            # multipart/form-data; boundary=---abc--\n            extra_headers = {\"Content-Type\": \"multipart/form-data\", **(extra_headers or {})}\n        return self._post(\n            \"/files/parser/create\",\n            body=maybe_transform(body, FileParserCreateParams),\n            files=files,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FileParserTaskCreateResp,\n        )\n\n    def content(\n            self,\n            task_id: str,\n            *,\n            format_type: Literal[\"text\", \"download_link\"],\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> _legacy_response.HttpxBinaryResponseContent:\n        \"\"\"\n        Returns the contents of the specified file.\n\n        Args:\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not task_id:\n            raise ValueError(f\"Expected a non-empty value for `task_id` but received {task_id!r}\")\n        extra_headers = {\"Accept\": \"application/binary\", **(extra_headers or {})}\n        return self._get(\n            f\"/files/parser/result/{task_id}/{format_type}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_legacy_binary_response.HttpxBinaryResponseContent,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/files.py",
    "content": "from __future__ import annotations\n\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast\nfrom typing_extensions import Literal\n\nfrom ..core import BaseAPI, maybe_transform\nfrom ..core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\n\nimport httpx\n\nfrom ..core import (\n    make_request_options,\n)\nfrom ..core import deepcopy_minimal, extract_files\nfrom ..types.files import FileObject, ListOfFileObject, file_create_params, FileDeleted\n\nfrom ..types.files import UploadDetail\n\nfrom ..core import _legacy_binary_response\nfrom ..core import _legacy_response\n\nif TYPE_CHECKING:\n    from .._client import ZhipuAI\n\n__all__ = [\"Files\", \"FilesWithRawResponse\"]\n\n\nclass Files(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            file: FileTypes = None,\n            upload_detail: List[UploadDetail] = None,\n            purpose: Literal[\"fine-tune\", \"retrieval\", \"batch\"],\n            knowledge_id: str = None,\n            sentence_size: int = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FileObject:\n\n        if not file and not upload_detail:\n            raise ValueError(\"At least one of `file` and `upload_detail` must be provided.\")\n        body = deepcopy_minimal(\n            {\n                \"file\": file,\n                \"upload_detail\": upload_detail,\n                \"purpose\": purpose,\n                \"knowledge_id\": knowledge_id,\n                \"sentence_size\": sentence_size,\n            }\n        )\n        files = extract_files(cast(Mapping[str, object], body), paths=[[\"file\"]])\n        if files:\n            # It should be noted that the actual Content-Type header that will be\n            # sent to the server will contain a `boundary` parameter, e.g.\n            # multipart/form-data; boundary=---abc--\n            extra_headers = {\"Content-Type\": \"multipart/form-data\", **(extra_headers or {})}\n        return self._post(\n            \"/files\",\n            body=maybe_transform(body, file_create_params.FileCreateParams),\n            files=files,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FileObject,\n        )\n\n    # def retrieve(\n    #         self,\n    #         file_id: str,\n    #         *,\n    #         extra_headers: Headers | None = None,\n    #         extra_body: Body | None = None,\n    #         timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    # ) -> FileObject:\n    #     \"\"\"\n    #     Returns information about a specific file.\n    #\n    #     Args:\n    #       file_id: The ID of the file to retrieve information about\n    #       extra_headers: Send extra headers\n    #\n    #       extra_body: Add additional JSON properties to the request\n    #\n    #       timeout: Override the client-level default timeout for this request, in seconds\n    #     \"\"\"\n    #     if not file_id:\n    #         raise ValueError(f\"Expected a non-empty value for `file_id` but received {file_id!r}\")\n    #     return self._get(\n    #         f\"/files/{file_id}\",\n    #         options=make_request_options(\n    #             extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n    #         ),\n    #         cast_type=FileObject,\n    #     )\n\n    def list(\n            self,\n            *,\n            purpose: str | NotGiven = NOT_GIVEN,\n            limit: int | NotGiven = NOT_GIVEN,\n            after: str | NotGiven = NOT_GIVEN,\n            order: str | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> ListOfFileObject:\n        return self._get(\n            \"/files\",\n            cast_type=ListOfFileObject,\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query={\n                    \"purpose\": purpose,\n                    \"limit\": limit,\n                    \"after\": after,\n                    \"order\": order,\n                },\n            ),\n        )\n\n    def delete(\n            self,\n            file_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FileDeleted:\n        \"\"\"\n        Delete a file.\n\n        Args:\n          file_id: The ID of the file to delete\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not file_id:\n            raise ValueError(f\"Expected a non-empty value for `file_id` but received {file_id!r}\")\n        return self._delete(\n            f\"/files/{file_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FileDeleted,\n        )\n\n    def content(\n            self,\n            file_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> _legacy_response.HttpxBinaryResponseContent:\n        \"\"\"\n        Returns the contents of the specified file.\n\n        Args:\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not file_id:\n            raise ValueError(f\"Expected a non-empty value for `file_id` but received {file_id!r}\")\n        extra_headers = {\"Accept\": \"application/binary\", **(extra_headers or {})}\n        return self._get(\n            f\"/files/{file_id}/content\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_legacy_binary_response.HttpxBinaryResponseContent,\n        )\n\n\nclass FilesWithRawResponse:\n    def __init__(self, files: Files) -> None:\n        self._files = files\n\n        self.create = _legacy_response.to_raw_response_wrapper(\n            files.create,\n        )\n        self.list = _legacy_response.to_raw_response_wrapper(\n            files.list,\n        )\n        self.content = _legacy_response.to_raw_response_wrapper(\n            files.content,\n        )"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/__init__.py",
    "content": "from .jobs import (\n    Jobs\n)\n\nfrom .models import (\n    FineTunedModels\n)\n\nfrom .fine_tuning import (\n    FineTuning\n)\n\n\n__all__ = [\n    'Jobs',\n    'FineTunedModels',\n    'FineTuning'\n]"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/fine_tuning.py",
    "content": "from typing import TYPE_CHECKING\nfrom .jobs import Jobs\nfrom .models import FineTunedModels\nfrom ...core import BaseAPI, cached_property\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n\nclass FineTuning(BaseAPI):\n\n    @cached_property\n    def jobs(self) -> Jobs:\n        return Jobs(self._client)\n\n    @cached_property\n    def models(self) -> FineTunedModels:\n        return FineTunedModels(self._client)\n\n"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/jobs/__init__.py",
    "content": "from .jobs import Jobs\n\n__all__ = [\n    \"Jobs\"\n]"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/jobs/jobs.py",
    "content": "from __future__ import annotations\n\nfrom typing import Optional, TYPE_CHECKING\n\nimport httpx\n\nfrom ....core import BaseAPI\nfrom ....core import NOT_GIVEN, Headers, NotGiven, Body\nfrom ....core import (\n    make_request_options,\n)\nfrom ....types.fine_tuning import (\n    FineTuningJob,\n    job_create_params,\n    ListOfFineTuningJob,\n    FineTuningJobEvent,\n)\n\nif TYPE_CHECKING:\n    from ...._client import ZhipuAI\n\n__all__ = [\"Jobs\"]\n\n\nclass Jobs(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            model: str,\n            training_file: str,\n            hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN,\n            suffix: Optional[str] | NotGiven = NOT_GIVEN,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            validation_file: Optional[str] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTuningJob:\n        return self._post(\n            \"/fine_tuning/jobs\",\n            body={\n                \"model\": model,\n                \"training_file\": training_file,\n                \"hyperparameters\": hyperparameters,\n                \"suffix\": suffix,\n                \"validation_file\": validation_file,\n                \"request_id\": request_id,\n            },\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FineTuningJob,\n        )\n\n    def retrieve(\n            self,\n            fine_tuning_job_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTuningJob:\n        return self._get(\n            f\"/fine_tuning/jobs/{fine_tuning_job_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FineTuningJob,\n        )\n\n    def list(\n            self,\n            *,\n            after: str | NotGiven = NOT_GIVEN,\n            limit: int | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> ListOfFineTuningJob:\n        return self._get(\n            \"/fine_tuning/jobs\",\n            cast_type=ListOfFineTuningJob,\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query={\n                    \"after\": after,\n                    \"limit\": limit,\n                },\n            ),\n        )\n\n    def cancel(\n            self,\n            fine_tuning_job_id: str,\n            *,\n            # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.\n            # The extra values given here take precedence over values defined on the client or passed to this method.\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTuningJob:\n\n        if not fine_tuning_job_id:\n            raise ValueError(f\"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}\")\n        return self._post(\n            f\"/fine_tuning/jobs/{fine_tuning_job_id}/cancel\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FineTuningJob,\n        )\n\n    def list_events(\n        self,\n        fine_tuning_job_id: str,\n        *,\n        after: str | NotGiven = NOT_GIVEN,\n        limit: int | NotGiven = NOT_GIVEN,\n        extra_headers: Headers | None = None,\n        extra_body: Body | None = None,\n        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTuningJobEvent:\n\n        return self._get(\n            f\"/fine_tuning/jobs/{fine_tuning_job_id}/events\",\n            cast_type=FineTuningJobEvent,\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query={\n                    \"after\": after,\n                    \"limit\": limit,\n                },\n            ),\n        )\n\n    def delete(\n            self,\n            fine_tuning_job_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTuningJob:\n\n        if not fine_tuning_job_id:\n            raise ValueError(f\"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}\")\n        return self._delete(\n            f\"/fine_tuning/jobs/{fine_tuning_job_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FineTuningJob,\n        )"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/models/__init__.py",
    "content": "from .fine_tuned_models import FineTunedModels\n\n__all__ = ['FineTunedModels']"
  },
  {
    "path": "zhipuai/api_resource/fine_tuning/models/fine_tuned_models.py",
    "content": "from __future__ import annotations\n\nfrom typing import Optional, TYPE_CHECKING\n\nimport httpx\n\nfrom ....core import BaseAPI\nfrom ....core import NOT_GIVEN, Headers, NotGiven, Body\nfrom ....core import (\n    make_request_options,\n)\n\nfrom ....types.fine_tuning.models import (\n    FineTunedModelsStatus\n)\n\nif TYPE_CHECKING:\n    from ...._client import ZhipuAI\n\n__all__ = [\"FineTunedModels\"]\n\n\nclass FineTunedModels(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def delete(\n            self,\n            fine_tuned_model: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> FineTunedModelsStatus:\n\n        if not fine_tuned_model:\n            raise ValueError(f\"Expected a non-empty value for `fine_tuned_model` but received {fine_tuned_model!r}\")\n        return self._delete(\n            f\"fine_tuning/fine_tuned_models/{fine_tuned_model}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=FineTunedModelsStatus,\n        )"
  },
  {
    "path": "zhipuai/api_resource/images.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, Optional, TYPE_CHECKING\n\nimport httpx\n\nfrom ..core import BaseAPI\nfrom ..core import NotGiven, NOT_GIVEN, Headers, Body\nfrom ..core import make_request_options\nfrom ..types.image import ImagesResponded\nfrom ..types.sensitive_word_check import SensitiveWordCheckRequest\n\nif TYPE_CHECKING:\n    from .._client import ZhipuAI\n\n\nclass Images(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def generations(\n            self,\n            *,\n            prompt: str,\n            model: str | NotGiven = NOT_GIVEN,\n            n: Optional[int] | NotGiven = NOT_GIVEN,\n            quality: Optional[str] | NotGiven = NOT_GIVEN,\n            response_format: Optional[str] | NotGiven = NOT_GIVEN,\n            size: Optional[str] | NotGiven = NOT_GIVEN,\n            style: Optional[str] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            user: str | NotGiven = NOT_GIVEN,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            disable_strict_validation: Optional[bool] | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> ImagesResponded:\n        _cast_type = ImagesResponded\n        if disable_strict_validation:\n            _cast_type = object\n        return self._post(\n            \"/images/generations\",\n            body={\n                \"prompt\": prompt,\n                \"model\": model,\n                \"n\": n,\n                \"quality\": quality,\n                \"response_format\": response_format,\n                \"sensitive_word_check\": sensitive_word_check,\n                \"size\": size,\n                \"style\": style,\n                \"user\": user,\n                \"user_id\": user_id,\n                \"request_id\": request_id,\n            },\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=_cast_type,\n            stream=False,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/knowledge/__init__.py",
    "content": "from .knowledge import Knowledge\n\n__all__ = ['Knowledge']"
  },
  {
    "path": "zhipuai/api_resource/knowledge/document/__init__.py",
    "content": "from .document import Document\n\n\n__all__ = ['Document']"
  },
  {
    "path": "zhipuai/api_resource/knowledge/document/document.py",
    "content": "from __future__ import annotations\n\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional, Dict\nfrom typing_extensions import Literal\n\nfrom ....core import BaseAPI, maybe_transform\nfrom ....core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\n\nimport httpx\n\nfrom ....core import (\n    make_request_options,\n)\nfrom ....core import deepcopy_minimal, extract_files\nfrom ....types.knowledge.document import DocumentData, DocumentObject, document_edit_params, document_list_params\n\nfrom ....types.files import UploadDetail, file_create_params\n\nfrom ....types.knowledge.document.document_list_resp import DocumentPage\n\nif TYPE_CHECKING:\n    from ...._client import ZhipuAI\n\n__all__ = [\"Document\"]\n\n\nclass Document(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            file: FileTypes = None,\n            custom_separator: Optional[List[str]] = None,\n            upload_detail: List[UploadDetail] = None,\n            purpose: Literal[\"retrieval\"],\n            knowledge_id: str = None,\n            sentence_size: int = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> DocumentObject:\n\n        if not file and not upload_detail:\n            raise ValueError(\"At least one of `file` and `upload_detail` must be provided.\")\n        body = deepcopy_minimal(\n            {\n                \"file\": file,\n                \"upload_detail\": upload_detail,\n                \"purpose\": purpose,\n                \"custom_separator\": custom_separator,\n                \"knowledge_id\": knowledge_id,\n                \"sentence_size\": sentence_size,\n            }\n        )\n        files = extract_files(cast(Mapping[str, object], body), paths=[[\"file\"]])\n        if files:\n            # It should be noted that the actual Content-Type header that will be\n            # sent to the server will contain a `boundary` parameter, e.g.\n            # multipart/form-data; boundary=---abc--\n            extra_headers = {\"Content-Type\": \"multipart/form-data\", **(extra_headers or {})}\n        return self._post(\n            \"/files\",\n            body=maybe_transform(body, file_create_params.FileCreateParams),\n            files=files,\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=DocumentObject,\n        )\n\n    def edit(\n            self,\n            document_id: str,\n            knowledge_type: str,\n            *,\n            custom_separator: Optional[List[str]] = None,\n            sentence_size: Optional[int] = None,\n            callback_url: Optional[str] = None,\n            callback_header: Optional[Dict[str, str]] = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> httpx.Response:\n        \"\"\"\n\n        Args:\n          document_id: 知识id\n          knowledge_type: 知识类型:\n                        1:文章知识: 支持pdf,url,docx\n                        2.问答知识-文档:  支持pdf,url,docx\n                        3.问答知识-表格:  支持xlsx\n                        4.商品库-表格:  支持xlsx\n                        5.自定义:  支持pdf,url,docx\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n          :param knowledge_type:\n          :param document_id:\n          :param timeout:\n          :param extra_body:\n          :param callback_header:\n          :param sentence_size:\n          :param extra_headers:\n          :param callback_url:\n          :param custom_separator:\n        \"\"\"\n        if not document_id:\n            raise ValueError(f\"Expected a non-empty value for `document_id` but received {document_id!r}\")\n\n        body = deepcopy_minimal(\n            {\n                \"id\": document_id,\n                \"knowledge_type\": knowledge_type,\n                \"custom_separator\": custom_separator,\n                \"sentence_size\": sentence_size,\n                \"callback_url\": callback_url,\n                \"callback_header\": callback_header,\n            }\n        )\n\n        return self._put(\n            f\"/document/{document_id}\",\n            body=maybe_transform(body, document_edit_params.DocumentEditParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=httpx.Response,\n        )\n\n    def list(\n            self,\n            knowledge_id: str,\n            *,\n            purpose: str | NotGiven = NOT_GIVEN,\n            page: str | NotGiven = NOT_GIVEN,\n            limit: str | NotGiven = NOT_GIVEN,\n            order: Literal[\"desc\", \"asc\"] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> DocumentPage:\n        return self._get(\n            \"/files\",\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query=maybe_transform(\n                    {\n                        \"knowledge_id\": knowledge_id,\n                        \"purpose\": purpose,\n                        \"page\": page,\n                        \"limit\": limit,\n                        \"order\": order,\n                    },\n                    document_list_params.DocumentListParams,\n                ),\n            ),\n            cast_type=DocumentPage,\n        )\n\n\n    def delete(\n            self,\n            document_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> httpx.Response:\n        \"\"\"\n        Delete a file.\n\n        Args:\n\n          document_id: 知识id\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not document_id:\n            raise ValueError(f\"Expected a non-empty value for `document_id` but received {document_id!r}\")\n\n        return self._delete(\n            f\"/document/{document_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=httpx.Response,\n        )\n\n    def retrieve(\n            self,\n            document_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> DocumentData:\n        \"\"\"\n\n        Args:\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not document_id:\n            raise ValueError(f\"Expected a non-empty value for `document_id` but received {document_id!r}\")\n\n        return self._get(\n            f\"/document/{document_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=DocumentData,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/knowledge/knowledge.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional\nfrom typing_extensions import Literal\n\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes, BaseAPI, maybe_transform\n\nimport httpx\n\nfrom ...core import (\n    make_request_options,\n)\nfrom ...core import deepcopy_minimal, cached_property\nfrom ...types.knowledge import knowledge_create_params, knowledge_list_params\nfrom ...types.knowledge import KnowledgeInfo, KnowledgeUsed\nfrom ...core.pagination import SyncCursorPage\nfrom .document import Document\nfrom ...types.knowledge.knowledge_list_resp import KnowledgePage\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"Knowledge\"]\n\n\nclass Knowledge(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    @cached_property\n    def document(self) -> Document:\n        return Document(self._client)\n\n    def create(\n            self,\n            embedding_id: int,\n            name: str,\n            *,\n            customer_identifier: Optional[str] = None,\n            description: Optional[str] = None,\n            background: Optional[Literal['blue', 'red', 'orange', 'purple', 'sky']] = None,\n            icon: Optional[Literal['question', 'book', 'seal', 'wrench', 'tag', 'horn', 'house']] = None,\n            bucket_id: Optional[str] = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> KnowledgeInfo:\n        body = deepcopy_minimal(\n            {\n                \"embedding_id\": embedding_id,\n                \"name\": name,\n                \"customer_identifier\": customer_identifier,\n                \"description\": description,\n                \"background\": background,\n                \"icon\": icon,\n                \"bucket_id\": bucket_id,\n\n            }\n        )\n        return self._post(\n            \"/knowledge\",\n            body=maybe_transform(body, knowledge_create_params.KnowledgeBaseParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=KnowledgeInfo,\n        )\n\n    def modify(\n            self,\n            knowledge_id: str,\n            embedding_id: int,\n            *,\n            name: str,\n            description: Optional[str] = None,\n            background: Optional[Literal['blue', 'red', 'orange', 'purple', 'sky']] = None,\n            icon: Optional[Literal['question', 'book', 'seal', 'wrench', 'tag', 'horn', 'house']] = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> httpx.Response:\n        body = deepcopy_minimal(\n            {\n                \"id\": knowledge_id,\n                \"embedding_id\": embedding_id,\n                \"name\": name,\n                \"description\": description,\n                \"background\": background,\n                \"icon\": icon,\n\n            }\n        )\n        return self._put(\n            f\"/knowledge/{knowledge_id}\",\n            body=maybe_transform(body, knowledge_create_params.KnowledgeBaseParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=httpx.Response,\n        )\n\n    def query(\n            self,\n            *,\n            page: int | NotGiven = 1,\n            size: int | NotGiven = 10,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> KnowledgePage:\n        return self._get(\n            \"/knowledge\",\n            options=make_request_options(\n                extra_headers=extra_headers,\n                extra_body=extra_body,\n                timeout=timeout,\n                query=maybe_transform(\n                    {\n                        \"page\": page,\n                        \"size\": size,\n                    },\n                    knowledge_list_params.KnowledgeListParams,\n                ),\n            ),\n            cast_type=KnowledgePage,\n        )\n\n    def delete(\n            self,\n            knowledge_id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> httpx.Response:\n        \"\"\"\n        Delete a file.\n\n        Args:\n          knowledge_id: 知识库ID\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        if not knowledge_id:\n            raise ValueError(f\"Expected a non-empty value for `knowledge_id`\")\n\n        return self._delete(\n            f\"/knowledge/{knowledge_id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=httpx.Response,\n        )\n\n    def used(\n            self,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> KnowledgeUsed:\n        \"\"\"\n        Returns the contents of the specified file.\n\n        Args:\n          extra_headers: Send extra headers\n\n          extra_body: Add additional JSON properties to the request\n\n          timeout: Override the client-level default timeout for this request, in seconds\n        \"\"\"\n        return self._get(\n            f\"/knowledge/capacity\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=KnowledgeUsed,\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/moderation/__init__.py",
    "content": "from .moderations import (\n    Moderations\n)\n\n__all__ = [\n    'Moderations'\n]"
  },
  {
    "path": "zhipuai/api_resource/moderation/moderations.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, List, TYPE_CHECKING, Dict\n\nimport logging\nfrom ...core import BaseAPI, deepcopy_minimal\nfrom ...types.moderation.moderation_completion import Completion\n\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"Moderations\"]\nclass Moderations(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def create(\n            self,\n            *,\n            model: str,\n            input: Union[str, List[str], Dict],\n    ) -> Completion:\n\n        body = deepcopy_minimal({\n            \"model\": model,\n            \"input\": input\n        })\n        return self._post(\n            \"/moderations\",\n            body=body,\n            cast_type=Completion\n        )\n\n\n"
  },
  {
    "path": "zhipuai/api_resource/tools/__init__.py",
    "content": "from .tools import Tools\n\n__all__ = ['Tools']"
  },
  {
    "path": "zhipuai/api_resource/tools/tools.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Union, Dict, Optional\nfrom typing_extensions import Literal\n\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, BaseAPI, maybe_transform, StreamResponse, deepcopy_minimal\n\nimport httpx\n\nfrom ...core import (\n    make_request_options,\n)\nimport logging\n\nfrom ...types.tools import tools_web_search_params, WebSearch, WebSearchChunk\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"Tools\"]\n\n\nclass Tools(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def web_search(\n            self,\n            *,\n            model: str,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,\n            messages: Union[str, List[str], List[int], object, None],\n            scope: Optional[str] | NotGiven = NOT_GIVEN,\n            location: Optional[str] | NotGiven = NOT_GIVEN,\n            recent_days: Optional[int] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> WebSearch | StreamResponse[WebSearchChunk]:\n\n        body = deepcopy_minimal(\n            {\n                \"model\": model,\n                \"request_id\": request_id,\n                \"messages\": messages,\n                \"stream\": stream,\n                \"scope\": scope,\n                \"location\": location,\n                \"recent_days\": recent_days,\n            })\n        return self._post(\n            \"/tools\",\n            body= maybe_transform(body, tools_web_search_params.WebSearchParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=WebSearch,\n            stream=stream or False,\n            stream_cls=StreamResponse[WebSearchChunk],\n        )\n"
  },
  {
    "path": "zhipuai/api_resource/videos/__init__.py",
    "content": "from .videos import (\n    Videos,\n)\n__all__ = [\n    'Videos',\n\n]"
  },
  {
    "path": "zhipuai/api_resource/videos/videos.py",
    "content": "from __future__ import annotations\n\n\nfrom typing import TYPE_CHECKING, List, Mapping, cast, Optional, Dict\nfrom typing_extensions import Literal\n\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\nfrom ...types.video import video_create_params\nfrom ...types.video import VideoObject\nfrom ...core import BaseAPI, maybe_transform\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven\n\nimport httpx\n\nfrom ...core import (\n    make_request_options,\n)\nfrom ...core import deepcopy_minimal, extract_files\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"Videos\"]\n\n\nclass Videos(BaseAPI):\n\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def generations(\n            self,\n            model: str,\n            *,\n            prompt: str = None,\n            image_url: object = None,\n            quality: str = None,\n            with_audio: bool = None,\n            size: str = None,\n            duration: int = None,\n            fps: int = None,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            request_id: str = None,\n            user_id: str = None,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> VideoObject:\n\n        if not model and not model:\n            raise ValueError(\"At least one of `model` and `prompt` must be provided.\")\n        body = deepcopy_minimal(\n            {\n                \"model\": model,\n                \"prompt\": prompt,\n                \"image_url\": image_url,\n                \"sensitive_word_check\": sensitive_word_check,\n                \"request_id\": request_id,\n                \"user_id\": user_id,\n                \"quality\": quality,\n                \"with_audio\": with_audio,\n                \"size\": size,\n                \"duration\": duration,\n                \"fps\": fps\n            }\n        )\n        return self._post(\n            \"/videos/generations\",\n            body=maybe_transform(body, video_create_params.VideoCreateParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=VideoObject,\n        )\n\n    def retrieve_videos_result(\n            self,\n            id: str,\n            *,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> VideoObject:\n\n        if not id:\n            raise ValueError(\"At least one of `id` must be provided.\")\n\n        return self._get(\n            f\"/async-result/{id}\",\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=VideoObject,\n        )"
  },
  {
    "path": "zhipuai/api_resource/web_search/__init__.py",
    "content": "from .web_search import WebSearchApi\n\n__all__ = ['WebSearchApi']"
  },
  {
    "path": "zhipuai/api_resource/web_search/web_search.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Union, Dict, Optional\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, BaseAPI, maybe_transform, StreamResponse, deepcopy_minimal\n\nimport httpx\n\nfrom ...core import (\n    make_request_options,\n)\nimport logging\n\nfrom ...types.web_search import web_search_create_params\nfrom ...types.web_search.web_search_resp import WebSearchResp\n\nlogger = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from ..._client import ZhipuAI\n\n__all__ = [\"WebSearchApi\"]\n\n\nclass WebSearchApi(BaseAPI):\n    def __init__(self, client: \"ZhipuAI\") -> None:\n        super().__init__(client)\n\n    def web_search(\n            self,\n            *,\n            request_id: Optional[str] | NotGiven = NOT_GIVEN,\n            search_engine: Optional[str] | NotGiven = NOT_GIVEN,\n            search_query: Optional[str] | NotGiven = NOT_GIVEN,\n            user_id: Optional[str] | NotGiven = NOT_GIVEN,\n            sensitive_word_check: Optional[SensitiveWordCheckRequest] | NotGiven = NOT_GIVEN,\n            count: Optional[int] | NotGiven = NOT_GIVEN,\n            search_domain_filter: Optional[str] | NotGiven = NOT_GIVEN,\n            search_recency_filter: Optional[str] | NotGiven = NOT_GIVEN,\n            content_size: Optional[str] | NotGiven = NOT_GIVEN,\n            search_intent: Optional[bool] | NotGiven = NOT_GIVEN,\n            location: Optional[str] | NotGiven = NOT_GIVEN,\n            extra_headers: Headers | None = None,\n            extra_body: Body | None = None,\n            timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n    ) -> WebSearchResp:\n\n        body = deepcopy_minimal(\n            {\n                \"request_id\": request_id,\n                \"search_engine\": search_engine,\n                \"search_query\": search_query,\n                \"user_id\": user_id,\n                \"sensitive_word_check\": sensitive_word_check,\n                \"count\":count,\n                \"search_domain_filter\": search_domain_filter,\n                \"search_recency_filter\": search_recency_filter,\n                \"content_size\": content_size,\n                \"search_intent\": search_intent,\n                \"location\": location\n            })\n        return self._post(\n            \"/web_search\",\n            body= maybe_transform(body, web_search_create_params.WebSearchCreatParams),\n            options=make_request_options(\n                extra_headers=extra_headers, extra_body=extra_body, timeout=timeout\n            ),\n            cast_type=WebSearchResp\n        )\n"
  },
  {
    "path": "zhipuai/core/__init__.py",
    "content": "from ._base_models import (\n    BaseModel,\n    construct_type\n)\nfrom ._base_api import BaseAPI\nfrom ._base_type import (\n    NOT_GIVEN,\n    Headers,\n    NotGiven,\n    Body,\n    IncEx,\n    ModelT,\n    Query,\n    FileTypes,\n\n)\nfrom ._base_compat import (\n    PYDANTIC_V2,\n    ConfigDict,\n    GenericModel,\n    get_args,\n    is_union,\n    parse_obj,\n    get_origin,\n    is_literal_type,\n    get_model_config,\n    get_model_fields,\n    field_get_default,\n    cached_property,\n)\nfrom ._files import (\n    is_file_content\n)\nfrom ._errors import (\n    ZhipuAIError,\n    APIStatusError,\n    APIRequestFailedError,\n    APIAuthenticationError,\n    APIReachLimitError,\n    APIInternalError,\n    APIServerFlowExceedError,\n    APIResponseError,\n    APIResponseValidationError,\n    APIConnectionError,\n    APITimeoutError,\n)\nfrom ._http_client import (\n    make_request_options,\n    HttpClient\n\n)\nfrom ._utils import (\n    is_list,\n    is_mapping,\n    parse_date,\n    parse_datetime,\n    is_given,\n    maybe_transform,\n    deepcopy_minimal,\n    extract_files,\n    drop_prefix_image_data,\n)\n\nfrom ._sse_client import StreamResponse\n\nfrom ._constants import (\n\n    ZHIPUAI_DEFAULT_TIMEOUT,\n    ZHIPUAI_DEFAULT_MAX_RETRIES,\n    ZHIPUAI_DEFAULT_LIMITS,\n)\n__all__ = [\n    \"BaseModel\",\n    \"construct_type\",\n    \"BaseAPI\",\n    \"NOT_GIVEN\",\n    \"Headers\",\n    \"NotGiven\",\n    \"Body\",\n    \"IncEx\",\n    \"ModelT\",\n    \"Query\",\n    \"FileTypes\",\n\n    \"PYDANTIC_V2\",\n    \"ConfigDict\",\n    \"GenericModel\",\n    \"get_args\",\n    \"is_union\",\n    \"parse_obj\",\n    \"get_origin\",\n    \"is_literal_type\",\n    \"get_model_config\",\n    \"get_model_fields\",\n    \"field_get_default\",\n\n    \"is_file_content\",\n\n    \"ZhipuAIError\",\n    \"APIStatusError\",\n    \"APIRequestFailedError\",\n    \"APIAuthenticationError\",\n    \"APIReachLimitError\",\n    \"APIInternalError\",\n    \"APIServerFlowExceedError\",\n    \"APIResponseError\",\n    \"APIResponseValidationError\",\n    \"APITimeoutError\",\n\n    \"make_request_options\",\n    \"HttpClient\",\n    \"ZHIPUAI_DEFAULT_TIMEOUT\",\n    \"ZHIPUAI_DEFAULT_MAX_RETRIES\",\n    \"ZHIPUAI_DEFAULT_LIMITS\",\n\n    \"is_list\",\n    \"is_mapping\",\n    \"parse_date\",\n    \"parse_datetime\",\n    \"is_given\",\n    \"maybe_transform\",\n\n    \"deepcopy_minimal\",\n    \"extract_files\",\n\n    \"StreamResponse\",\n\n]\n"
  },
  {
    "path": "zhipuai/core/_base_api.py",
    "content": "from __future__ import annotations\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._client import ZhipuAI\n\n\nclass BaseAPI:\n    _client: ZhipuAI\n\n    def __init__(self, client: ZhipuAI) -> None:\n        self._client = client\n        self._delete = client.delete\n        self._get = client.get\n        self._post = client.post\n        self._put = client.put\n        self._patch = client.patch\n        self._get_api_list = client.get_api_list\n"
  },
  {
    "path": "zhipuai/core/_base_compat.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload\nfrom datetime import date, datetime\nfrom typing_extensions import Self\n\nimport pydantic\nfrom pydantic.fields import FieldInfo\n\nfrom zhipuai.core._base_type import StrBytesIntFloat\n\n_T = TypeVar(\"_T\")\n_ModelT = TypeVar(\"_ModelT\", bound=pydantic.BaseModel)\n\n# --------------- Pydantic v2 compatibility ---------------\n\n# Pyright incorrectly reports some of our functions as overriding a method when they don't\n# pyright: reportIncompatibleMethodOverride=false\n\nPYDANTIC_V2 = pydantic.VERSION.startswith(\"2.\")\n\n# v1 re-exports\nif TYPE_CHECKING:\n\n    def parse_date(value: date | StrBytesIntFloat) -> date:  # noqa: ARG001\n        ...\n\n    def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:  # noqa: ARG001\n        ...\n\n    def get_args(t: type[Any]) -> tuple[Any, ...]:  # noqa: ARG001\n        ...\n\n    def is_union(tp: type[Any] | None) -> bool:  # noqa: ARG001\n        ...\n\n    def get_origin(t: type[Any]) -> type[Any] | None:  # noqa: ARG001\n        ...\n\n    def is_literal_type(type_: type[Any]) -> bool:  # noqa: ARG001\n        ...\n\n    def is_typeddict(type_: type[Any]) -> bool:  # noqa: ARG001\n        ...\n\nelse:\n    if PYDANTIC_V2:\n        from pydantic.v1.typing import (\n            get_args as get_args,\n            is_union as is_union,\n            get_origin as get_origin,\n            is_typeddict as is_typeddict,\n            is_literal_type as is_literal_type,\n        )\n        from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime\n    else:\n        from pydantic.typing import (\n            get_args as get_args,\n            is_union as is_union,\n            get_origin as get_origin,\n            is_typeddict as is_typeddict,\n            is_literal_type as is_literal_type,\n        )\n        from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime\n\n\n# refactored config\nif TYPE_CHECKING:\n    from pydantic import ConfigDict as ConfigDict\nelse:\n    if PYDANTIC_V2:\n        from pydantic import ConfigDict\n    else:\n        # TODO: provide an error message here?\n        ConfigDict = None\n\n\n# renamed methods / properties\ndef parse_obj(model: type[_ModelT], value: object) -> _ModelT:\n    if PYDANTIC_V2:\n        return model.model_validate(value)\n    else:\n        return cast(_ModelT, model.parse_obj(value))  # pyright: ignore[reportDeprecated, reportUnnecessaryCast]\n\n\ndef field_is_required(field: FieldInfo) -> bool:\n    if PYDANTIC_V2:\n        return field.is_required()\n    return field.required  # type: ignore\n\n\ndef field_get_default(field: FieldInfo) -> Any:\n    value = field.get_default()\n    if PYDANTIC_V2:\n        from pydantic_core import PydanticUndefined\n\n        if value == PydanticUndefined:\n            return None\n        return value\n    return value\n\n\ndef field_outer_type(field: FieldInfo) -> Any:\n    if PYDANTIC_V2:\n        return field.annotation\n    return field.outer_type_  # type: ignore\n\n\ndef get_model_config(model: type[pydantic.BaseModel]) -> Any:\n    if PYDANTIC_V2:\n        return model.model_config\n    return model.__config__  # type: ignore\n\n\ndef get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]:\n    if PYDANTIC_V2:\n        return model.model_fields\n    return model.__fields__  # type: ignore\n\n\ndef model_copy(model: _ModelT) -> _ModelT:\n    if PYDANTIC_V2:\n        return model.model_copy()\n    return model.copy()  # type: ignore\n\n\ndef model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:\n    if PYDANTIC_V2:\n        return model.model_dump_json(indent=indent)\n    return model.json(indent=indent)  # type: ignore\n\n\ndef model_dump(\n    model: pydantic.BaseModel,\n    *,\n    exclude_unset: bool = False,\n    exclude_defaults: bool = False,\n) -> dict[str, Any]:\n    if PYDANTIC_V2:\n        return model.model_dump(\n            exclude_unset=exclude_unset,\n            exclude_defaults=exclude_defaults,\n        )\n    return cast(\n        \"dict[str, Any]\",\n        model.dict(  # pyright: ignore[reportDeprecated, reportUnnecessaryCast]\n            exclude_unset=exclude_unset,\n            exclude_defaults=exclude_defaults,\n        ),\n    )\n\n\ndef model_parse(model: type[_ModelT], data: Any) -> _ModelT:\n    if PYDANTIC_V2:\n        return model.model_validate(data)\n    return model.parse_obj(data)  # pyright: ignore[reportDeprecated]\n\n\n# generic models\nif TYPE_CHECKING:\n\n    class GenericModel(pydantic.BaseModel):\n        ...\n\nelse:\n    if PYDANTIC_V2:\n        # there no longer needs to be a distinction in v2 but\n        # we still have to create our own subclass to avoid\n        # inconsistent MRO ordering errors\n        class GenericModel(pydantic.BaseModel):\n            ...\n\n    else:\n        import pydantic.generics\n\n        class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel):\n            ...\n\n\n# cached properties\nif TYPE_CHECKING:\n    cached_property = property\n\n    # we define a separate type (copied from typeshed)\n    # that represents that `cached_property` is `set`able\n    # at runtime, which differs from `@property`.\n    #\n    # this is a separate type as editors likely special case\n    # `@property` and we don't want to cause issues just to have\n    # more helpful internal types.\n\n    class typed_cached_property(Generic[_T]):\n        func: Callable[[Any], _T]\n        attrname: str | None\n\n        def __init__(self, func: Callable[[Any], _T]) -> None:\n            ...\n\n        @overload\n        def __get__(self, instance: None, owner: type[Any] | None = None) -> Self:\n            ...\n\n        @overload\n        def __get__(self, instance: object, owner: type[Any] | None = None) -> _T:\n            ...\n\n        def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self:\n            raise NotImplementedError()\n\n        def __set_name__(self, owner: type[Any], name: str) -> None:\n            ...\n\n        # __set__ is not defined at runtime, but @cached_property is designed to be settable\n        def __set__(self, instance: object, value: _T) -> None:\n            ...\nelse:\n    try:\n        from functools import cached_property as cached_property\n    except ImportError:\n        from cached_property import cached_property as cached_property\n\n    typed_cached_property = cached_property\n"
  },
  {
    "path": "zhipuai/core/_base_models.py",
    "content": "from __future__ import annotations\n\nimport inspect\nimport os\nfrom typing import TYPE_CHECKING, Any, Type, Generic, TypeVar, cast, Dict, Callable\nfrom datetime import date, datetime\nfrom typing_extensions import (\n    Literal,\n    ClassVar,\n    Protocol,\n    override,\n    runtime_checkable, TypeGuard, ParamSpec,\n)\n\nimport pydantic\nimport pydantic.generics\nfrom pydantic.fields import FieldInfo\nfrom pydantic import root_validator\nfrom ._base_type import (\n    IncEx,\n    ModelT,\n)\nfrom ._utils import is_list, is_mapping, parse_date, parse_datetime, is_annotated_type, extract_type_arg, \\\n    strip_annotated_type, PropertyInfo, coerce_boolean\nfrom ._base_compat import (\n    PYDANTIC_V2,\n    ConfigDict,\n    GenericModel as BaseGenericModel,\n    get_args,\n    is_union,\n    parse_obj,\n    get_origin,\n    is_literal_type,\n    get_model_config,\n    get_model_fields,\n    field_get_default,\n)\n\nif TYPE_CHECKING:\n    from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema\n\n__all__ = [\"BaseModel\", \"GenericModel\"]\n_BaseModelT = TypeVar(\"_BaseModelT\", bound=\"BaseModel\")\n\n_T = TypeVar(\"_T\")\nP = ParamSpec(\"P\")\n\n\n@runtime_checkable\nclass _ConfigProtocol(Protocol):\n    allow_population_by_field_name: bool\n\n\nclass BaseModel(pydantic.BaseModel):\n    if PYDANTIC_V2:\n        model_config: ClassVar[ConfigDict] = ConfigDict(\n            extra=\"allow\", defer_build=coerce_boolean(os.environ.get(\"DEFER_PYDANTIC_BUILD\", \"true\"))\n        )\n    else:\n\n        @property\n        @override\n        def model_fields_set(self) -> set[str]:\n            # a forwards-compat shim for pydantic v2\n            return self.__fields_set__  # type: ignore\n\n        class Config(pydantic.BaseConfig):  # pyright: ignore[reportDeprecated]\n            extra: Any = pydantic.Extra.allow  # type: ignore\n\n    def to_dict(\n            self,\n            *,\n            mode: Literal[\"json\", \"python\"] = \"python\",\n            use_api_names: bool = True,\n            exclude_unset: bool = True,\n            exclude_defaults: bool = False,\n            exclude_none: bool = False,\n            warnings: bool = True,\n    ) -> dict[str, object]:\n        \"\"\"Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude.\n\n        By default, fields that were not set by the API will not be included,\n        and keys will match the API response, *not* the property names from the model.\n\n        For example, if the API responds with `\"fooBar\": true` but we've defined a `foo_bar: bool` property,\n        the output will use the `\"fooBar\"` key (unless `use_api_names=False` is passed).\n\n        Args:\n            mode:\n                If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `\"2024-3-22T18:11:19.117000Z\"`.\n                If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)`\n\n            use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`.\n            exclude_unset: Whether to exclude fields that have not been explicitly set.\n            exclude_defaults: Whether to exclude fields that are set to their default value from the output.\n            exclude_none: Whether to exclude fields that have a value of `None` from the output.\n            warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2.\n        \"\"\"\n        return self.model_dump(\n            mode=mode,\n            by_alias=use_api_names,\n            exclude_unset=exclude_unset,\n            exclude_defaults=exclude_defaults,\n            exclude_none=exclude_none,\n            warnings=warnings,\n        )\n\n    def to_json(\n            self,\n            *,\n            indent: int | None = 2,\n            use_api_names: bool = True,\n            exclude_unset: bool = True,\n            exclude_defaults: bool = False,\n            exclude_none: bool = False,\n            warnings: bool = True,\n    ) -> str:\n        \"\"\"Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation).\n\n        By default, fields that were not set by the API will not be included,\n        and keys will match the API response, *not* the property names from the model.\n\n        For example, if the API responds with `\"fooBar\": true` but we've defined a `foo_bar: bool` property,\n        the output will use the `\"fooBar\"` key (unless `use_api_names=False` is passed).\n\n        Args:\n            indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2`\n            use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`.\n            exclude_unset: Whether to exclude fields that have not been explicitly set.\n            exclude_defaults: Whether to exclude fields that have the default value.\n            exclude_none: Whether to exclude fields that have a value of `None`.\n            warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2.\n        \"\"\"\n        return self.model_dump_json(\n            indent=indent,\n            by_alias=use_api_names,\n            exclude_unset=exclude_unset,\n            exclude_defaults=exclude_defaults,\n            exclude_none=exclude_none,\n            warnings=warnings,\n        )\n\n    @override\n    def __str__(self) -> str:\n        # mypy complains about an invalid self arg\n        return f'{self.__repr_name__()}({self.__repr_str__(\", \")})'  # type: ignore[misc]\n\n    # Override the 'construct' method in a way that supports recursive parsing without validation.\n    # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836.\n    @classmethod\n    @override\n    def construct(\n            cls: Type[ModelT],\n            _fields_set: set[str] | None = None,\n            **values: object,\n    ) -> ModelT:\n        m = cls.__new__(cls)\n        fields_values: dict[str, object] = {}\n\n        config = get_model_config(cls)\n        populate_by_name = (\n            config.allow_population_by_field_name\n            if isinstance(config, _ConfigProtocol)\n            else config.get(\"populate_by_name\")\n        )\n\n        if _fields_set is None:\n            _fields_set = set()\n\n        model_fields = get_model_fields(cls)\n        for name, field in model_fields.items():\n            key = field.alias\n            if key is None or (key not in values and populate_by_name):\n                key = name\n\n            if key in values:\n                fields_values[name] = _construct_field(value=values[key], field=field, key=key)\n                _fields_set.add(name)\n            else:\n                fields_values[name] = field_get_default(field)\n\n        _extra = {}\n        for key, value in values.items():\n            if key not in model_fields:\n                if PYDANTIC_V2:\n                    _extra[key] = value\n                else:\n                    _fields_set.add(key)\n                    fields_values[key] = value\n\n        object.__setattr__(m, \"__dict__\", fields_values)\n\n        if PYDANTIC_V2:\n            # these properties are copied from Pydantic's `model_construct()` method\n            object.__setattr__(m, \"__pydantic_private__\", None)\n            object.__setattr__(m, \"__pydantic_extra__\", _extra)\n            object.__setattr__(m, \"__pydantic_fields_set__\", _fields_set)\n        else:\n            # init_private_attributes() does not exist in v2\n            m._init_private_attributes()  # type: ignore\n\n            # copied from Pydantic v1's `construct()` method\n            object.__setattr__(m, \"__fields_set__\", _fields_set)\n\n        return m\n\n    if not TYPE_CHECKING:\n        # type checkers incorrectly complain about this assignment\n        # because the type signatures are technically different\n        # although not in practice\n        model_construct = construct\n\n    if not PYDANTIC_V2:\n        # we define aliases for some of the new pydantic v2 methods so\n        # that we can just document these methods without having to specify\n        # a specific pydantic version as some users may not know which\n        # pydantic version they are currently using\n\n        @override\n        def model_dump(\n                self,\n                *,\n                mode: Literal[\"json\", \"python\"] | str = \"python\",\n                include: IncEx = None,\n                exclude: IncEx = None,\n                by_alias: bool = False,\n                exclude_unset: bool = False,\n                exclude_defaults: bool = False,\n                exclude_none: bool = False,\n                round_trip: bool = False,\n                warnings: bool | Literal[\"none\", \"warn\", \"error\"] = True,\n                context: dict[str, Any] | None = None,\n                serialize_as_any: bool = False,\n        ) -> dict[str, Any]:\n            \"\"\"Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump\n\n            Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.\n\n            Args:\n                mode: The mode in which `to_python` should run.\n                    If mode is 'json', the dictionary will only contain JSON serializable types.\n                    If mode is 'python', the dictionary may contain any Python objects.\n                include: A list of fields to include in the output.\n                exclude: A list of fields to exclude from the output.\n                by_alias: Whether to use the field's alias in the dictionary key if defined.\n                exclude_unset: Whether to exclude fields that are unset or None from the output.\n                exclude_defaults: Whether to exclude fields that are set to their default value from the output.\n                exclude_none: Whether to exclude fields that have a value of `None` from the output.\n                round_trip: Whether to enable serialization and deserialization round-trip support.\n                warnings: Whether to log warnings when invalid fields are encountered.\n\n            Returns:\n                A dictionary representation of the model.\n            \"\"\"\n            if mode != \"python\":\n                raise ValueError(\"mode is only supported in Pydantic v2\")\n            if round_trip != False:\n                raise ValueError(\"round_trip is only supported in Pydantic v2\")\n            if warnings != True:\n                raise ValueError(\"warnings is only supported in Pydantic v2\")\n            if context is not None:\n                raise ValueError(\"context is only supported in Pydantic v2\")\n            if serialize_as_any != False:\n                raise ValueError(\"serialize_as_any is only supported in Pydantic v2\")\n            return super().dict(  # pyright: ignore[reportDeprecated]\n                include=include,\n                exclude=exclude,\n                by_alias=by_alias,\n                exclude_unset=exclude_unset,\n                exclude_defaults=exclude_defaults,\n                exclude_none=exclude_none,\n            )\n\n        @override\n        def model_dump_json(\n                self,\n                *,\n                indent: int | None = None,\n                include: IncEx = None,\n                exclude: IncEx = None,\n                by_alias: bool = False,\n                exclude_unset: bool = False,\n                exclude_defaults: bool = False,\n                exclude_none: bool = False,\n                round_trip: bool = False,\n                warnings: bool | Literal[\"none\", \"warn\", \"error\"] = True,\n                context: dict[str, Any] | None = None,\n                serialize_as_any: bool = False,\n        ) -> str:\n            \"\"\"Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json\n\n            Generates a JSON representation of the model using Pydantic's `to_json` method.\n\n            Args:\n                indent: Indentation to use in the JSON output. If None is passed, the output will be compact.\n                include: Field(s) to include in the JSON output. Can take either a string or set of strings.\n                exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings.\n                by_alias: Whether to serialize using field aliases.\n                exclude_unset: Whether to exclude fields that have not been explicitly set.\n                exclude_defaults: Whether to exclude fields that have the default value.\n                exclude_none: Whether to exclude fields that have a value of `None`.\n                round_trip: Whether to use serialization/deserialization between JSON and class instance.\n                warnings: Whether to show any warnings that occurred during serialization.\n\n            Returns:\n                A JSON string representation of the model.\n            \"\"\"\n            if round_trip != False:\n                raise ValueError(\"round_trip is only supported in Pydantic v2\")\n            if warnings != True:\n                raise ValueError(\"warnings is only supported in Pydantic v2\")\n            if context is not None:\n                raise ValueError(\"context is only supported in Pydantic v2\")\n            if serialize_as_any != False:\n                raise ValueError(\"serialize_as_any is only supported in Pydantic v2\")\n            return super().json(  # type: ignore[reportDeprecated]\n                indent=indent,\n                include=include,\n                exclude=exclude,\n                by_alias=by_alias,\n                exclude_unset=exclude_unset,\n                exclude_defaults=exclude_defaults,\n                exclude_none=exclude_none,\n            )\n\n\ndef _construct_field(value: object, field: FieldInfo, key: str) -> object:\n    if value is None:\n        return field_get_default(field)\n\n    if PYDANTIC_V2:\n        type_ = field.annotation\n    else:\n        type_ = cast(type, field.outer_type_)  # type: ignore\n\n    if type_ is None:\n        raise RuntimeError(f\"Unexpected field type is None for {key}\")\n\n    return construct_type(value=value, type_=type_)\n\n\ndef is_basemodel(type_: type) -> bool:\n    \"\"\"Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`\"\"\"\n    if is_union(type_):\n        for variant in get_args(type_):\n            if is_basemodel(variant):\n                return True\n\n        return False\n\n    return is_basemodel_type(type_)\n\n\ndef is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]:\n    origin = get_origin(type_) or type_\n    return issubclass(origin, BaseModel) or issubclass(origin, GenericModel)\n\n\ndef build(\n        base_model_cls: Callable[P, _BaseModelT],\n        *args: P.args,\n        **kwargs: P.kwargs,\n) -> _BaseModelT:\n    \"\"\"Construct a BaseModel class without validation.\n\n    This is useful for cases where you need to instantiate a `BaseModel`\n    from an API response as this provides type-safe params which isn't supported\n    by helpers like `construct_type()`.\n\n    ```py\n    build(MyModel, my_field_a=\"foo\", my_field_b=123)\n    ```\n    \"\"\"\n    if args:\n        raise TypeError(\n            \"Received positional arguments which are not supported; Keyword arguments must be used instead\",\n        )\n\n    return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs))\n\n\ndef construct_type_unchecked(*, value: object, type_: type[_T]) -> _T:\n    \"\"\"Loose coercion to the expected type with construction of nested values.\n\n    Note: the returned value from this function is not guaranteed to match the\n    given type.\n    \"\"\"\n    return cast(_T, construct_type(value=value, type_=type_))\n\n\ndef construct_type(*, value: object, type_: type) -> object:\n    \"\"\"Loose coercion to the expected type with construction of nested values.\n\n    If the given value does not match the expected type then it is returned as-is.\n    \"\"\"\n    # we allow `object` as the input type because otherwise, passing things like\n    # `Literal['value']` will be reported as a type error by type checkers\n    type_ = cast(\"type[object]\", type_)\n\n    # unwrap `Annotated[T, ...]` -> `T`\n    if is_annotated_type(type_):\n        meta: tuple[Any, ...] = get_args(type_)[1:]\n        type_ = extract_type_arg(type_, 0)\n    else:\n        meta = tuple()\n    # we need to use the origin class for any types that are subscripted generics\n    # e.g. Dict[str, object]\n    origin = get_origin(type_) or type_\n    args = get_args(type_)\n\n    if is_union(origin):\n        try:\n            return validate_type(type_=cast(\"type[object]\", type_), value=value)\n        except Exception:\n            pass\n\n        # if the type is a discriminated union then we want to construct the right variant\n        # in the union, even if the data doesn't match exactly, otherwise we'd break code\n        # that relies on the constructed class types, e.g.\n        #\n        # class FooType:\n        #   kind: Literal['foo']\n        #   value: str\n        #\n        # class BarType:\n        #   kind: Literal['bar']\n        #   value: int\n        #\n        # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then\n        # we'd end up constructing `FooType` when it should be `BarType`.\n        discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta)\n        if discriminator and is_mapping(value):\n            variant_value = value.get(discriminator.field_alias_from or discriminator.field_name)\n            if variant_value and isinstance(variant_value, str):\n                variant_type = discriminator.mapping.get(variant_value)\n                if variant_type:\n                    return construct_type(type_=variant_type, value=value)\n\n        # if the data is not valid, use the first variant that doesn't fail while deserializing\n        for variant in args:\n            try:\n                return construct_type(value=value, type_=variant)\n            except Exception:\n                continue\n\n        raise RuntimeError(f\"Could not convert data into a valid instance of {type_}\")\n    if origin == dict:\n        if not is_mapping(value):\n            return value\n\n        _, items_type = get_args(type_)  # Dict[_, items_type]\n        return {key: construct_type(value=item, type_=items_type) for key, item in value.items()}\n\n    if not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)):\n        if is_list(value):\n            return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value]\n\n        if is_mapping(value):\n            if issubclass(type_, BaseModel):\n                return type_.construct(**value)  # type: ignore[arg-type]\n\n            return cast(Any, type_).construct(**value)\n\n    if origin == list:\n        if not is_list(value):\n            return value\n\n        inner_type = args[0]  # List[inner_type]\n        return [construct_type(value=entry, type_=inner_type) for entry in value]\n\n    if origin == float:\n        if isinstance(value, int):\n            coerced = float(value)\n            if coerced != value:\n                return value\n            return coerced\n\n        return value\n\n    if type_ == datetime:\n        try:\n            return parse_datetime(value)  # type: ignore\n        except Exception:\n            return value\n\n    if type_ == date:\n        try:\n            return parse_date(value)  # type: ignore\n        except Exception:\n            return value\n\n    return value\n\n\n@runtime_checkable\nclass CachedDiscriminatorType(Protocol):\n    __discriminator__: DiscriminatorDetails\n\n\nclass DiscriminatorDetails:\n    field_name: str\n    \"\"\"The name of the discriminator field in the variant class, e.g.\n\n    ```py\n    class Foo(BaseModel):\n        type: Literal['foo']\n    ```\n\n    Will result in field_name='type'\n    \"\"\"\n\n    field_alias_from: str | None\n    \"\"\"The name of the discriminator field in the API response, e.g.\n\n    ```py\n    class Foo(BaseModel):\n        type: Literal['foo'] = Field(alias='type_from_api')\n    ```\n\n    Will result in field_alias_from='type_from_api'\n    \"\"\"\n\n    mapping: dict[str, type]\n    \"\"\"Mapping of discriminator value to variant type, e.g.\n\n    {'foo': FooVariant, 'bar': BarVariant}\n    \"\"\"\n\n    def __init__(\n            self,\n            *,\n            mapping: dict[str, type],\n            discriminator_field: str,\n            discriminator_alias: str | None,\n    ) -> None:\n        self.mapping = mapping\n        self.field_name = discriminator_field\n        self.field_alias_from = discriminator_alias\n\n\ndef _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:\n    if isinstance(union, CachedDiscriminatorType):\n        return union.__discriminator__\n\n    discriminator_field_name: str | None = None\n\n    for annotation in meta_annotations:\n        if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None:\n            discriminator_field_name = annotation.discriminator\n            break\n\n    if not discriminator_field_name:\n        return None\n\n    mapping: dict[str, type] = {}\n    discriminator_alias: str | None = None\n\n    for variant in get_args(union):\n        variant = strip_annotated_type(variant)\n        if is_basemodel_type(variant):\n            if PYDANTIC_V2:\n                field = _extract_field_schema_pv2(variant, discriminator_field_name)\n                if not field:\n                    continue\n\n                # Note: if one variant defines an alias then they all should\n                discriminator_alias = field.get(\"serialization_alias\")\n\n                field_schema = field[\"schema\"]\n\n                if field_schema[\"type\"] == \"literal\":\n                    for entry in cast(\"LiteralSchema\", field_schema)[\"expected\"]:\n                        if isinstance(entry, str):\n                            mapping[entry] = variant\n            else:\n                field_info = cast(\"dict[str, FieldInfo]\", variant.__fields__).get(\n                    discriminator_field_name)  # pyright: ignore[reportDeprecated, reportUnnecessaryCast]\n                if not field_info:\n                    continue\n\n                # Note: if one variant defines an alias then they all should\n                discriminator_alias = field_info.alias\n\n                if field_info.annotation and is_literal_type(field_info.annotation):\n                    for entry in get_args(field_info.annotation):\n                        if isinstance(entry, str):\n                            mapping[entry] = variant\n\n    if not mapping:\n        return None\n\n    details = DiscriminatorDetails(\n        mapping=mapping,\n        discriminator_field=discriminator_field_name,\n        discriminator_alias=discriminator_alias,\n    )\n    cast(CachedDiscriminatorType, union).__discriminator__ = details\n    return details\n\n\ndef _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None:\n    schema = model.__pydantic_core_schema__\n    if schema[\"type\"] != \"model\":\n        return None\n\n    fields_schema = schema[\"schema\"]\n    if fields_schema[\"type\"] != \"model-fields\":\n        return None\n\n    fields_schema = cast(\"ModelFieldsSchema\", fields_schema)\n\n    field = fields_schema[\"fields\"].get(field_name)\n    if not field:\n        return None\n\n    return cast(\"ModelField\", field)  # pyright: ignore[reportUnnecessaryCast]\n\n\ndef validate_type(*, type_: type[_T], value: object) -> _T:\n    \"\"\"Strict validation that the given value matches the expected type\"\"\"\n    if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel):\n        return cast(_T, parse_obj(type_, value))\n\n    return cast(_T, _validate_non_model_type(type_=type_, value=value))\n\n\n# our use of subclasssing here causes weirdness for type checkers,\n# so we just pretend that we don't subclass\nif TYPE_CHECKING:\n    GenericModel = BaseModel\nelse:\n\n    class GenericModel(BaseGenericModel, BaseModel):\n        pass\n\nif PYDANTIC_V2:\n    from pydantic import TypeAdapter\n\n\n    def _validate_non_model_type(*, type_: type[_T], value: object) -> _T:\n        return TypeAdapter(type_).validate_python(value)\n\nelif not TYPE_CHECKING:\n\n    class TypeAdapter(Generic[_T]):\n        \"\"\"Used as a placeholder to easily convert runtime types to a Pydantic format\n           to provide validation.\n\n           For example:\n           ```py\n           validated = RootModel[int](__root__=\"5\").__root__\n           # validated: 5\n           ```\n           \"\"\"\n\n        def __init__(self, type_: Type[_T]):\n            self.type_ = type_\n\n        def validate_python(self, value: Any) -> _T:\n            if not isinstance(value, self.type_):\n                raise ValueError(f\"Invalid type: {value} is not of type {self.type_}\")\n            return value\n\n\n    def _validate_non_model_type(*, type_: type[_T], value: object) -> _T:\n        return TypeAdapter(type_).validate_python(value)\n"
  },
  {
    "path": "zhipuai/core/_base_type.py",
    "content": "from __future__ import annotations\n\nfrom os import PathLike\nfrom typing import (\n    IO,\n    TYPE_CHECKING,\n    Any,\n    Dict,\n    List,\n    Type,\n    Tuple,\n    Union,\n    Mapping,\n    TypeVar,\n    Callable,\n    Optional,\n    Sequence,\n)\nimport pydantic\nfrom httpx import Response\nfrom typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable\n\nQuery = Mapping[str, object]\nBody = object\nAnyMapping = Mapping[str, object]\nPrimitiveData = Union[str, int, float, bool, None]\nData = Union[PrimitiveData, List[Any], Tuple[Any], \"Mapping[str, Any]\"]\nModelT = TypeVar(\"ModelT\", bound=pydantic.BaseModel)\n_T = TypeVar(\"_T\")\n\nif TYPE_CHECKING:\n    NoneType: Type[None]\nelse:\n    NoneType = type(None)\n\n\n# Sentinel class used until PEP 0661 is accepted\nclass NotGiven:\n    \"\"\"\n    A sentinel singleton class used to distinguish omitted keyword arguments\n    from those passed in with the value None (which may have different behavior).\n\n    For example:\n\n    ```py\n    def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ...\n\n    get(timeout=1) # 1s timeout\n    get(timeout=None) # No timeout\n    get() # Default timeout behavior, which may not be statically known at the method definition.\n    ```\n    \"\"\"\n\n    def __bool__(self) -> Literal[False]:\n        return False\n\n    @override\n    def __repr__(self) -> str:\n        return \"NOT_GIVEN\"\n\n\nNotGivenOr = Union[_T, NotGiven]\nNOT_GIVEN = NotGiven()\n\n\nclass Omit:\n    \"\"\"In certain situations you need to be able to represent a case where a default value has\n    to be explicitly removed and `None` is not an appropriate substitute, for example:\n\n    ```py\n    # as the default `Content-Type` header is `application/json` that will be sent\n    client.post('/upload/files', files={'file': b'my raw file content'})\n\n    # you can't explicitly override the header as it has to be dynamically generated\n    # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'\n    client.post(..., headers={'Content-Type': 'multipart/form-data'})\n\n    # instead you can remove the default `application/json` header by passing Omit\n    client.post(..., headers={'Content-Type': Omit()})\n    ```\n    \"\"\"\n\n    def __bool__(self) -> Literal[False]:\n        return False\n\n\n@runtime_checkable\nclass ModelBuilderProtocol(Protocol):\n    @classmethod\n    def build(\n            cls: type[_T],\n            *,\n            response: Response,\n            data: object,\n    ) -> _T:\n        ...\n\n\nHeaders = Mapping[str, Union[str, Omit]]\n\n\nclass HeadersLikeProtocol(Protocol):\n    def get(self, __key: str) -> str | None:\n        ...\n\n\nHeadersLike = Union[Headers, HeadersLikeProtocol]\n\nResponseT = TypeVar(\n    \"ResponseT\",\n    bound=\"Union[str, None, BaseModel, List[Any], Dict[str, Any], Response, UnknownResponse, ModelBuilderProtocol, BinaryResponseContent]\",\n)\n\nStrBytesIntFloat = Union[str, bytes, int, float]\n\n# Note: copied from Pydantic\n# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49\nIncEx: TypeAlias = \"set[int] | set[str] | dict[int, Any] | dict[str, Any] | None\"\n\nPostParser = Callable[[Any], Any]\n\n\n@runtime_checkable\nclass InheritsGeneric(Protocol):\n    \"\"\"Represents a type that has inherited from `Generic`\n\n    The `__orig_bases__` property can be used to determine the resolved\n    type variable for a given base class.\n    \"\"\"\n\n    __orig_bases__: tuple[_GenericAlias]\n\n\nclass _GenericAlias(Protocol):\n    __origin__: type[object]\n\n\nclass HttpxSendArgs(TypedDict, total=False):\n    auth: httpx.Auth\n\n\n# for user input files\nif TYPE_CHECKING:\n    Base64FileInput = Union[IO[bytes], PathLike[str]]\n    FileContent = Union[IO[bytes], bytes, PathLike[str]]\nelse:\n    Base64FileInput = Union[IO[bytes], PathLike]\n    FileContent = Union[IO[bytes], bytes, PathLike]\n\nFileTypes = Union[\n    # file (or bytes)\n    FileContent,\n        # (filename, file (or bytes))\n    Tuple[Optional[str], FileContent],\n        # (filename, file (or bytes), content_type)\n    Tuple[Optional[str], FileContent, Optional[str]],\n        # (filename, file (or bytes), content_type, headers)\n    Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]],\n]\nRequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]]\n\n# duplicate of the above but without our custom file support\nHttpxFileContent = Union[bytes, IO[bytes]]\nHttpxFileTypes = Union[\n    # file (or bytes)\n    HttpxFileContent,\n        # (filename, file (or bytes))\n    Tuple[Optional[str], HttpxFileContent],\n        # (filename, file (or bytes), content_type)\n    Tuple[Optional[str], HttpxFileContent, Optional[str]],\n        # (filename, file (or bytes), content_type, headers)\n    Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]],\n]\n\nHttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]]\n"
  },
  {
    "path": "zhipuai/core/_constants.py",
    "content": "import httpx\n\nRAW_RESPONSE_HEADER = \"X-Stainless-Raw-Response\"\n# 通过 `Timeout` 控制接口`connect` 和 `read` 超时时间，默认为`timeout=300.0, connect=8.0`\nZHIPUAI_DEFAULT_TIMEOUT = httpx.Timeout(timeout=300.0, connect=8.0)\n# 通过 `retry` 参数控制重试次数，默认为3次\nZHIPUAI_DEFAULT_MAX_RETRIES = 3\n# 通过 `Limits` 控制最大连接数和保持连接数，默认为`max_connections=50, max_keepalive_connections=10`\nZHIPUAI_DEFAULT_LIMITS = httpx.Limits(max_connections=50, max_keepalive_connections=10)\n\nINITIAL_RETRY_DELAY = 0.5\nMAX_RETRY_DELAY = 8.0\n"
  },
  {
    "path": "zhipuai/core/_errors.py",
    "content": "from __future__ import annotations\n\nimport httpx\n\n__all__ = [\n    \"ZhipuAIError\",\n    \"APIStatusError\",\n    \"APIRequestFailedError\",\n    \"APIAuthenticationError\",\n    \"APIReachLimitError\",\n    \"APIInternalError\",\n    \"APIServerFlowExceedError\",\n    \"APIResponseError\",\n    \"APIResponseValidationError\",\n    \"APITimeoutError\",\n    \"APIConnectionError\",\n]\n\n\nclass ZhipuAIError(Exception):\n    def __init__(self, message: str, ) -> None:\n        super().__init__(message)\n\n\nclass APIStatusError(ZhipuAIError):\n    response: httpx.Response\n    status_code: int\n\n    def __init__(self, message: str, *, response: httpx.Response) -> None:\n        super().__init__(message)\n        self.response = response\n        self.status_code = response.status_code\n\n\nclass APIRequestFailedError(APIStatusError):\n    ...\n\n\nclass APIAuthenticationError(APIStatusError):\n    ...\n\n\nclass APIReachLimitError(APIStatusError):\n    ...\n\n\nclass APIInternalError(APIStatusError):\n    ...\n\n\nclass APIServerFlowExceedError(APIStatusError):\n    ...\n\n\nclass APIResponseError(ZhipuAIError):\n    message: str\n    request: httpx.Request\n    json_data: object\n\n    def __init__(self, message: str, request: httpx.Request, json_data: object):\n        self.message = message\n        self.request = request\n        self.json_data = json_data\n        super().__init__(message)\n\n\nclass APIResponseValidationError(APIResponseError):\n    status_code: int\n    response: httpx.Response\n\n    def __init__(\n            self,\n            response: httpx.Response,\n            json_data: object | None, *,\n            message: str | None = None\n    ) -> None:\n        super().__init__(\n            message=message or \"Data returned by API invalid for expected schema.\",\n            request=response.request,\n            json_data=json_data\n        )\n        self.response = response\n        self.status_code = response.status_code\n\n\nclass APIConnectionError(APIResponseError):\n    def __init__(self, *, message: str = \"Connection error.\", request: httpx.Request) -> None:\n        super().__init__(message, request, json_data=None)\n\n\nclass APITimeoutError(APIConnectionError):\n    def __init__(self, request: httpx.Request) -> None:\n        super().__init__(message=\"Request timed out.\", request=request)\n"
  },
  {
    "path": "zhipuai/core/_files.py",
    "content": "from __future__ import annotations\n\nimport io\nimport os\nimport pathlib\nfrom typing import Mapping, Sequence, overload\nfrom typing_extensions import TypeGuard\n\nfrom ._base_type import (\n    FileTypes,\n    HttpxFileTypes,\n    HttpxRequestFiles,\n    RequestFiles,\n    Base64FileInput, FileContent, HttpxFileContent,\n)\nfrom ._utils import is_tuple_t, is_mapping_t, is_sequence_t\n\n\ndef is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:\n    return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)\n\n\ndef is_file_content(obj: object) -> TypeGuard[FileContent]:\n    return (\n            isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj,\n                                                                                                         os.PathLike)\n    )\n\n\ndef assert_is_file_content(obj: object, *, key: str | None = None) -> None:\n    if not is_file_content(obj):\n        prefix = f\"Expected entry at `{key}`\" if key is not None else f\"Expected file input `{obj!r}`\"\n        raise RuntimeError(\n            f\"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads\"\n        ) from None\n\n\n@overload\ndef to_httpx_files(files: None) -> None:\n    ...\n\n\n@overload\ndef to_httpx_files(files: RequestFiles) -> HttpxRequestFiles:\n    ...\n\n\ndef to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:\n    if files is None:\n        return None\n\n    if is_mapping_t(files):\n        files = {key: _transform_file(file) for key, file in files.items()}\n    elif is_sequence_t(files):\n        files = [(key, _transform_file(file)) for key, file in files]\n    else:\n        raise TypeError(f\"Unexpected file type input {type(files)}, expected mapping or sequence\")\n\n    return files\n\n\ndef _transform_file(file: FileTypes) -> HttpxFileTypes:\n    if is_file_content(file):\n        if isinstance(file, os.PathLike):\n            path = pathlib.Path(file)\n            return (path.name, path.read_bytes())\n\n        return file\n\n    if is_tuple_t(file):\n        return (file[0], _read_file_content(file[1]), *file[2:])\n\n    raise TypeError(f\"Expected file types input to be a FileContent type or to be a tuple\")\n\n\ndef _read_file_content(file: FileContent) -> HttpxFileContent:\n    if isinstance(file, os.PathLike):\n        return pathlib.Path(file).read_bytes()\n    return file\n"
  },
  {
    "path": "zhipuai/core/_http_client.py",
    "content": "# -*- coding:utf-8 -*-\nfrom __future__ import annotations\n\nimport inspect\nimport warnings\nfrom typing import (\n    Any,\n    Type,\n    Union,\n    cast,\n    Mapping,\n    TypeVar,\n    Dict,\n    overload, Optional, Literal, Generic, Iterator, TYPE_CHECKING\n)\n\nfrom random import random\nimport time\nimport httpx\nimport pydantic\nfrom httpx import URL, Timeout\n\nfrom . import _errors, get_origin\nfrom ._base_compat import model_copy\nfrom ._base_type import (\n    NotGiven,\n    ResponseT,\n    Body,\n    Headers,\n    NOT_GIVEN,\n    RequestFiles,\n    Query,\n    Data,\n    Omit,\n    AnyMapping,\n    ModelBuilderProtocol,\n    HttpxSendArgs, PostParser,\n)\nfrom ._constants import ZHIPUAI_DEFAULT_MAX_RETRIES, ZHIPUAI_DEFAULT_LIMITS, ZHIPUAI_DEFAULT_TIMEOUT, \\\n    INITIAL_RETRY_DELAY, MAX_RETRY_DELAY, RAW_RESPONSE_HEADER\nfrom ._errors import APIResponseValidationError, APIStatusError, APITimeoutError, APIConnectionError\nfrom ._files import to_httpx_files\nfrom ._legacy_response import LegacyAPIResponse\nfrom ._request_opt import FinalRequestOptions, UserRequestInput\nfrom ._response import BaseAPIResponse, APIResponse, extract_response_type\nfrom ._sse_client import StreamResponse\nfrom ._utils import flatten, is_mapping, is_given\nfrom ._base_models import construct_type, GenericModel, validate_type\nimport logging\n\n\nlog: logging.Logger = logging.getLogger(__name__)\n\n# TODO: make base page type vars covariant\nSyncPageT = TypeVar(\"SyncPageT\", bound=\"BaseSyncPage[Any]\")\n# AsyncPageT = TypeVar(\"AsyncPageT\", bound=\"BaseAsyncPage[Any]\")\n\n_T = TypeVar(\"_T\")\n_T_co = TypeVar(\"_T_co\", covariant=True)\n\nif TYPE_CHECKING:\n    from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT\nelse:\n    try:\n        from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT\n    except ImportError:\n        # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366\n        HTTPX_DEFAULT_TIMEOUT = Timeout(5.0)\n\n\nheaders = {\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json; charset=UTF-8\",\n}\n\n\n\nclass PageInfo:\n    \"\"\"Stores the necessary information to build the request to retrieve the next page.\n\n    Either `url` or `params` must be set.\n    \"\"\"\n\n    url: URL | NotGiven\n    params: Query | NotGiven\n\n    @overload\n    def __init__(\n            self,\n            *,\n            url: URL,\n    ) -> None:\n        ...\n\n    @overload\n    def __init__(\n            self,\n            *,\n            params: Query,\n    ) -> None:\n        ...\n\n    def __init__(\n            self,\n            *,\n            url: URL | NotGiven = NOT_GIVEN,\n            params: Query | NotGiven = NOT_GIVEN,\n    ) -> None:\n        self.url = url\n        self.params = params\n\n\nclass BasePage(GenericModel, Generic[_T]):\n    \"\"\"\n    Defines the core interface for pagination.\n\n    Type Args:\n        ModelT: The pydantic model that represents an item in the response.\n\n    Methods:\n        has_next_page(): Check if there is another page available\n        next_page_info(): Get the necessary information to make a request for the next page\n    \"\"\"\n\n    _options: FinalRequestOptions = pydantic.PrivateAttr()\n    _model: Type[_T] = pydantic.PrivateAttr()\n\n    def has_next_page(self) -> bool:\n        items = self._get_page_items()\n        if not items:\n            return False\n        return self.next_page_info() is not None\n\n    def next_page_info(self) -> Optional[PageInfo]:\n        ...\n\n    def _get_page_items(self) -> Iterable[_T]:  # type: ignore[empty-body]\n        ...\n\n    def _params_from_url(self, url: URL) -> httpx.QueryParams:\n        # TODO: do we have to preprocess params here?\n        return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params)\n\n    def _info_to_options(self, info: PageInfo) -> FinalRequestOptions:\n        options = model_copy(self._options)\n        options._strip_raw_response_header()\n\n        if not isinstance(info.params, NotGiven):\n            options.params = {**options.params, **info.params}\n            return options\n\n        if not isinstance(info.url, NotGiven):\n            params = self._params_from_url(info.url)\n            url = info.url.copy_with(params=params)\n            options.params = dict(url.params)\n            options.url = str(url)\n            return options\n\n        raise ValueError(\"Unexpected PageInfo state\")\n\n\nclass BaseSyncPage(BasePage[_T], Generic[_T]):\n    _client: HttpClient = pydantic.PrivateAttr()\n\n    def _set_private_attributes(\n            self,\n            client: HttpClient,\n            model: Type[_T],\n            options: FinalRequestOptions,\n    ) -> None:\n        self._model = model\n        self._client = client\n        self._options = options\n\n    # Pydantic uses a custom `__iter__` method to support casting BaseModels\n    # to dictionaries. e.g. dict(model).\n    # As we want to support `for item in page`, this is inherently incompatible\n    # with the default pydantic behaviour. It is not possible to support both\n    # use cases at once. Fortunately, this is not a big deal as all other pydantic\n    # methods should continue to work as expected as there is an alternative method\n    # to cast a model to a dictionary, model.dict(), which is used internally\n    # by pydantic.\n    def __iter__(self) -> Iterator[_T]:  # type: ignore\n        for page in self.iter_pages():\n            for item in page._get_page_items():\n                yield item\n\n    def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]:\n        page = self\n        while True:\n            yield page\n            if page.has_next_page():\n                page = page.get_next_page()\n            else:\n                return\n\n    def get_next_page(self: SyncPageT) -> SyncPageT:\n        info = self.next_page_info()\n        if not info:\n            raise RuntimeError(\n                \"No next page expected; please check `.has_next_page()` before calling `.get_next_page()`.\"\n            )\n\n        options = self._info_to_options(info)\n        return self._client._request_api_list(self._model, page=self.__class__, options=options)\n\n\nclass HttpClient:\n    _client: httpx.Client\n    _version: str\n    _base_url: URL\n    max_retries: int\n    timeout: Union[float, Timeout, None]\n    _limits: httpx.Limits\n    _has_custom_http_client: bool\n    _default_stream_cls: Type[StreamResponse[Any]] | None = None\n\n    _strict_response_validation: bool\n\n    def __init__(\n            self,\n            *,\n            version: str,\n            base_url: URL,\n            _strict_response_validation: bool,\n            max_retries: int = ZHIPUAI_DEFAULT_MAX_RETRIES,\n            timeout: Union[float, Timeout, None],\n            limits: httpx.Limits | None = None,\n            custom_httpx_client: httpx.Client | None = None,\n            custom_headers: Mapping[str, str] | None = None,\n    ) -> None:\n        if limits is not None:\n            warnings.warn(\n                \"The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead\",\n                category=DeprecationWarning,\n                stacklevel=3,\n            )\n            if custom_httpx_client is not None:\n                raise ValueError(\"The `http_client` argument is mutually exclusive with `connection_pool_limits`\")\n        else:\n            limits = ZHIPUAI_DEFAULT_LIMITS\n\n        if not is_given(timeout):\n            if custom_httpx_client and custom_httpx_client.timeout != HTTPX_DEFAULT_TIMEOUT:\n                timeout = custom_httpx_client.timeout\n            else:\n                timeout = ZHIPUAI_DEFAULT_TIMEOUT\n        self.max_retries = max_retries\n        self.timeout = timeout\n        self._limits = limits\n        self._has_custom_http_client = bool(custom_httpx_client)\n        self._client = custom_httpx_client or httpx.Client(\n            base_url=base_url,\n            timeout=self.timeout,\n            limits=limits,\n        )\n        self._version = version\n        url = URL(url=base_url)\n        if not url.raw_path.endswith(b\"/\"):\n            url = url.copy_with(raw_path=url.raw_path + b\"/\")\n        self._base_url = url\n        self._custom_headers = custom_headers or {}\n        self._strict_response_validation = _strict_response_validation\n\n    def _prepare_url(self, url: str) -> URL:\n\n        sub_url = URL(url)\n        if sub_url.is_relative_url:\n            request_raw_url = self._base_url.raw_path + sub_url.raw_path.lstrip(b\"/\")\n            return self._base_url.copy_with(raw_path=request_raw_url)\n\n        return sub_url\n\n    @property\n    def _default_headers(self):\n        return \\\n            {\n                \"Accept\": \"application/json\",\n                \"Content-Type\": \"application/json; charset=UTF-8\",\n                \"ZhipuAI-SDK-Ver\": self._version,\n                \"source_type\": \"zhipu-sdk-python\",\n                \"x-request-sdk\": \"zhipu-sdk-python\",\n                **self.auth_headers,\n                **self._custom_headers,\n            }\n\n    @property\n    def custom_auth(self) -> httpx.Auth | None:\n        return None\n\n    @property\n    def auth_headers(self):\n        return {}\n\n    def _prepare_headers(self, options: FinalRequestOptions) -> httpx.Headers:\n        custom_headers = options.headers or {}\n        headers_dict = _merge_mappings(self._default_headers, custom_headers)\n\n        httpx_headers = httpx.Headers(headers_dict)\n\n        return httpx_headers\n\n    def _remaining_retries(\n            self,\n            remaining_retries: Optional[int],\n            options: FinalRequestOptions,\n    ) -> int:\n        return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries)\n\n    def _calculate_retry_timeout(\n            self,\n            remaining_retries: int,\n            options: FinalRequestOptions,\n            response_headers: Optional[httpx.Headers] = None,\n    ) -> float:\n        max_retries = options.get_max_retries(self.max_retries)\n\n        # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says.\n        # retry_after = self._parse_retry_after_header(response_headers)\n        # if retry_after is not None and 0 < retry_after <= 60:\n        #     return retry_after\n\n        nb_retries = max_retries - remaining_retries\n\n        # Apply exponential backoff, but not more than the max.\n        sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY)\n\n        # Apply some jitter, plus-or-minus half a second.\n        jitter = 1 - 0.25 * random()\n        timeout = sleep_seconds * jitter\n        return timeout if timeout >= 0 else 0\n\n    def _build_request(\n            self,\n            options: FinalRequestOptions\n    ) -> httpx.Request:\n        kwargs: dict[str, Any] = {}\n        headers = self._prepare_headers(options)\n        url = self._prepare_url(options.url)\n        json_data = options.json_data\n        if options.extra_json is not None:\n            if json_data is None:\n                json_data = cast(Body, options.extra_json)\n            elif is_mapping(json_data):\n                json_data = _merge_mappings(json_data, options.extra_json)\n            else:\n                raise RuntimeError(f\"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`\")\n\n        content_type = headers.get(\"Content-Type\")\n        # multipart/form-data; boundary=---abc--\n        if headers.get(\"Content-Type\") == \"multipart/form-data\":\n            if \"boundary\" not in content_type:\n                # only remove the header if the boundary hasn't been explicitly set\n                # as the caller doesn't want httpx to come up with their own boundary\n                headers.pop(\"Content-Type\")\n\n            if json_data:\n                kwargs[\"data\"] = self._make_multipartform(json_data)\n\n        return self._client.build_request(\n            headers=headers,\n            timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout,\n            method=options.method,\n            url=url,\n            json=json_data,\n            files=options.files,\n            params=options.params,\n            **kwargs,\n        )\n\n    def _object_to_formfata(self, key: str, value: Data | Mapping[object, object]) -> list[tuple[str, str]]:\n        items = []\n\n        if isinstance(value, Mapping):\n            for k, v in value.items():\n                items.extend(self._object_to_formfata(f\"{key}[{k}]\", v))\n            return items\n        if isinstance(value, (list, tuple)):\n            for v in value:\n                items.extend(self._object_to_formfata(key + \"[]\", v))\n            return items\n\n        def _primitive_value_to_str(val) -> str:\n            # copied from httpx\n            if val is True:\n                return \"true\"\n            elif val is False:\n                return \"false\"\n            elif val is None:\n                return \"\"\n            return str(val)\n\n        str_data = _primitive_value_to_str(value)\n\n        if not str_data:\n            return []\n        return [(key, str_data)]\n\n    def _make_multipartform(self, data: Mapping[object, object]) -> dict[str, object]:\n\n        items = flatten([self._object_to_formfata(k, v) for k, v in data.items()])\n\n        serialized: dict[str, object] = {}\n        for key, value in items:\n            if key in serialized:\n                raise ValueError(f\"存在重复的键: {key};\")\n            serialized[key] = value\n        return serialized\n\n\n    def _process_response_data(\n            self,\n            *,\n            data: object,\n            cast_type: Type[ResponseT],\n            response: httpx.Response,\n    ) -> ResponseT:\n\n        if data is None:\n            return cast(ResponseT, None)\n\n        if cast_type is object:\n            return cast(ResponseT, data)\n\n        try:\n            if inspect.isclass(cast_type) and issubclass(cast_type, ModelBuilderProtocol):\n                return cast(ResponseT, cast_type.build(response=response, data=data))\n\n            if self._strict_response_validation:\n                return cast(ResponseT, validate_type(type_=cast_type, value=data))\n\n            return cast(ResponseT, construct_type(type_=cast_type, value=data))\n        except pydantic.ValidationError as err:\n            raise APIResponseValidationError(response=response, json_data=data) from err\n\n    def _should_stream_response_body(self, request: httpx.Request) -> bool:\n        return request.headers.get(RAW_RESPONSE_HEADER) == \"stream\"  # type: ignore[no-any-return]\n\n    def _should_retry(self, response: httpx.Response) -> bool:\n        # Note: this is not a standard header\n        should_retry_header = response.headers.get(\"x-should-retry\")\n\n        # If the server explicitly says whether or not to retry, obey.\n        if should_retry_header == \"true\":\n            log.debug(\"Retrying as header `x-should-retry` is set to `true`\")\n            return True\n        if should_retry_header == \"false\":\n            log.debug(\"Not retrying as header `x-should-retry` is set to `false`\")\n            return False\n\n        # Retry on request timeouts.\n        if response.status_code == 408:\n            log.debug(\"Retrying due to status code %i\", response.status_code)\n            return True\n\n        # Retry on lock timeouts.\n        if response.status_code == 409:\n            log.debug(\"Retrying due to status code %i\", response.status_code)\n            return True\n\n        # Retry on rate limits.\n        if response.status_code == 429:\n            log.debug(\"Retrying due to status code %i\", response.status_code)\n            return True\n\n        # Retry internal errors.\n        if response.status_code >= 500:\n            log.debug(\"Retrying due to status code %i\", response.status_code)\n            return True\n\n        log.debug(\"Not retrying\")\n        return False\n\n    def is_closed(self) -> bool:\n        return self._client.is_closed\n\n    def close(self):\n        self._client.close()\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.close()\n\n    def request(\n            self,\n            cast_type: Type[ResponseT],\n            options: FinalRequestOptions,\n            remaining_retries: Optional[int] = None,\n            *,\n            stream: bool = False,\n            stream_cls: Type[StreamResponse] | None = None,\n    ) -> ResponseT | StreamResponse:\n        return self._request(\n            cast_type=cast_type,\n            options=options,\n            stream=stream,\n            stream_cls=stream_cls,\n            remaining_retries=remaining_retries,\n        )\n\n    def _request(\n            self,\n            *,\n            cast_type: Type[ResponseT],\n            options: FinalRequestOptions,\n            remaining_retries: int | None,\n            stream: bool,\n            stream_cls: Type[StreamResponse] | None,\n    ) -> ResponseT | StreamResponse:\n\n        retries = self._remaining_retries(remaining_retries, options)\n        request = self._build_request(options)\n\n        kwargs: HttpxSendArgs = {}\n        if self.custom_auth is not None:\n            kwargs[\"auth\"] = self.custom_auth\n        try:\n            response = self._client.send(\n                request,\n                stream=stream or self._should_stream_response_body(request=request),\n                **kwargs,\n            )\n        except httpx.TimeoutException as err:\n            log.debug(\"Encountered httpx.TimeoutException\", exc_info=True)\n\n            if retries > 0:\n                return self._retry_request(\n                    options,\n                    cast_type,\n                    retries,\n                    stream=stream,\n                    stream_cls=stream_cls,\n                    response_headers=None,\n                )\n\n            log.debug(\"Raising timeout error\")\n            raise APITimeoutError(request=request) from err\n        except Exception as err:\n            log.debug(\"Encountered Exception\", exc_info=True)\n\n            if retries > 0:\n                return self._retry_request(\n                    options,\n                    cast_type,\n                    retries,\n                    stream=stream,\n                    stream_cls=stream_cls,\n                    response_headers=None,\n                )\n\n            log.debug(\"Raising connection error\")\n            raise APIConnectionError(request=request) from err\n\n        log.debug(\n            'HTTP Request: %s %s \"%i %s\"', request.method, request.url, response.status_code, response.reason_phrase\n        )\n\n        try:\n            response.raise_for_status()\n        except httpx.HTTPStatusError as err:  # thrown on 4xx and 5xx status code\n            log.debug(\"Encountered httpx.HTTPStatusError\", exc_info=True)\n\n            if retries > 0 and self._should_retry(err.response):\n                err.response.close()\n                return self._retry_request(\n                    options,\n                    cast_type,\n                    retries,\n                    err.response.headers,\n                    stream=stream,\n                    stream_cls=stream_cls,\n                )\n\n            # If the response is streamed then we need to explicitly read the response\n            # to completion before attempting to access the response text.\n            if not err.response.is_closed:\n                err.response.read()\n\n            log.debug(\"Re-raising status error\")\n            raise self._make_status_error(err.response) from None\n\n        # return self._parse_response(\n        #     cast_type=cast_type,\n        #     options=options,\n        #     response=response,\n        #     stream=stream,\n        #     stream_cls=stream_cls,\n        # )\n        return self._process_response(\n            cast_type=cast_type,\n            options=options,\n            response=response,\n            stream=stream,\n            stream_cls=stream_cls,\n        )\n\n    def _retry_request(\n            self,\n            options: FinalRequestOptions,\n            cast_type: Type[ResponseT],\n            remaining_retries: int,\n            response_headers: httpx.Headers | None,\n            *,\n            stream: bool,\n            stream_cls: Type[StreamResponse] | None,\n    ) -> ResponseT | StreamResponse:\n        remaining = remaining_retries - 1\n        if remaining == 1:\n            log.debug(\"1 retry left\")\n        else:\n            log.debug(\"%i retries left\", remaining)\n\n        timeout = self._calculate_retry_timeout(remaining, options, response_headers)\n        log.info(\"Retrying request to %s in %f seconds\", options.url, timeout)\n\n        # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a\n        # different thread if necessary.\n        time.sleep(timeout)\n\n        return self._request(\n            options=options,\n            cast_type=cast_type,\n            remaining_retries=remaining,\n            stream=stream,\n            stream_cls=stream_cls,\n        )\n\n    def _process_response(\n            self,\n            *,\n            cast_type: Type[ResponseT],\n            options: FinalRequestOptions,\n            response: httpx.Response,\n            stream: bool,\n            stream_cls: Type[StreamResponse] | None,\n    ) -> ResponseT:\n        # _legacy_response with raw_response_header to paser method\n        if response.request.headers.get(RAW_RESPONSE_HEADER) == \"true\":\n            return cast(\n                ResponseT,\n                LegacyAPIResponse(\n                    raw=response,\n                    client=self,\n                    cast_type=cast_type,\n                    stream=stream,\n                    stream_cls=stream_cls,\n                    options=options,\n                ),\n            )\n\n        origin = get_origin(cast_type) or cast_type\n\n        if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):\n            if not issubclass(origin, APIResponse):\n                raise TypeError(f\"API Response types must subclass {APIResponse}; Received {origin}\")\n\n            response_cls = cast(\"type[BaseAPIResponse[Any]]\", cast_type)\n            return cast(\n                ResponseT,\n                response_cls(\n                    raw=response,\n                    client=self,\n                    cast_type=extract_response_type(response_cls),\n                    stream=stream,\n                    stream_cls=stream_cls,\n                    options=options,\n                ),\n            )\n\n        if cast_type == httpx.Response:\n            return cast(ResponseT, response)\n\n        api_response = APIResponse(\n            raw=response,\n            client=self,\n            cast_type=cast(\"type[ResponseT]\", cast_type),  # pyright: ignore[reportUnnecessaryCast]\n            stream=stream,\n            stream_cls=stream_cls,\n            options=options,\n        )\n        if bool(response.request.headers.get(RAW_RESPONSE_HEADER)):\n            return cast(ResponseT, api_response)\n\n        return api_response.parse()\n\n    def _request_api_list(\n            self,\n            model: Type[object],\n            page: Type[SyncPageT],\n            options: FinalRequestOptions,\n    ) -> SyncPageT:\n        def _parser(resp: SyncPageT) -> SyncPageT:\n            resp._set_private_attributes(\n                client=self,\n                model=model,\n                options=options,\n            )\n            return resp\n\n        options.post_parser = _parser\n\n        return self.request(page, options, stream=False)\n\n    @overload\n    def get(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            options: UserRequestInput = {},\n            stream: Literal[False] = False,\n    ) -> ResponseT:\n        ...\n\n    @overload\n    def get(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            options: UserRequestInput = {},\n            stream: Literal[True],\n            stream_cls: Type[StreamResponse],\n    ) -> StreamResponse:\n        ...\n\n    @overload\n    def get(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            options: UserRequestInput = {},\n            stream: bool,\n            stream_cls: Type[StreamResponse] | None = None,\n    ) -> ResponseT | StreamResponse:\n        ...\n\n    def get(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            options: UserRequestInput = {},\n            stream: bool = False,\n            stream_cls: Type[StreamResponse] | None = None,\n    ) -> ResponseT:\n        opts = FinalRequestOptions.construct(method=\"get\", url=path, **options)\n        return cast(ResponseT, self.request(cast_type, opts, stream=stream, stream_cls=stream_cls))\n\n    @overload\n    def post(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            files: RequestFiles | None = None,\n            stream: Literal[False] = False,\n    ) -> ResponseT:\n        ...\n\n    @overload\n    def post(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            files: RequestFiles | None = None,\n            stream: Literal[True],\n            stream_cls: Type[StreamResponse],\n    ) -> StreamResponse:\n        ...\n\n    @overload\n    def post(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            files: RequestFiles | None = None,\n            stream: bool,\n            stream_cls: Type[StreamResponse] | None = None,\n    ) -> ResponseT | StreamResponse:\n        ...\n\n    def post(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            files: RequestFiles | None = None,\n            stream: bool = False,\n            stream_cls: Type[StreamResponse[Any]] | None = None,\n    ) -> ResponseT | StreamResponse:\n        opts = FinalRequestOptions.construct(\n            method=\"post\", url=path, json_data=body, files=to_httpx_files(files), **options\n        )\n\n        return cast(ResponseT, self.request(cast_type, opts, stream=stream, stream_cls=stream_cls))\n\n    def patch(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n    ) -> ResponseT:\n        opts = FinalRequestOptions.construct(method=\"patch\", url=path, json_data=body, **options)\n\n        return self.request(\n            cast_type=cast_type, options=opts,\n        )\n\n    def put(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            files: RequestFiles | None = None,\n    ) -> ResponseT | StreamResponse:\n        opts = FinalRequestOptions.construct(method=\"put\", url=path, json_data=body, files=to_httpx_files(files),\n                                             **options)\n\n        return self.request(\n            cast_type=cast_type, options=opts,\n        )\n\n    def delete(\n            self,\n            path: str,\n            *,\n            cast_type: Type[ResponseT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n    ) -> ResponseT | StreamResponse:\n        opts = FinalRequestOptions.construct(method=\"delete\", url=path, json_data=body, **options)\n\n        return self.request(\n            cast_type=cast_type, options=opts,\n        )\n\n    def get_api_list(\n            self,\n            path: str,\n            *,\n            model: Type[object],\n            page: Type[SyncPageT],\n            body: Body | None = None,\n            options: UserRequestInput = {},\n            method: str = \"get\",\n    ) -> SyncPageT:\n        opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options)\n        return self._request_api_list(model, page, opts)\n\n    def _make_status_error(self, response) -> APIStatusError:\n        response_text = response.text.strip()\n        status_code = response.status_code\n        error_msg = f\"Error code: {status_code}, with error text {response_text}\"\n\n        if status_code == 400:\n            return _errors.APIRequestFailedError(message=error_msg, response=response)\n        elif status_code == 401:\n            return _errors.APIAuthenticationError(message=error_msg, response=response)\n        elif status_code == 429:\n            return _errors.APIReachLimitError(message=error_msg, response=response)\n        elif status_code == 500:\n            return _errors.APIInternalError(message=error_msg, response=response)\n        elif status_code == 503:\n            return _errors.APIServerFlowExceedError(message=error_msg, response=response)\n        return APIStatusError(message=error_msg, response=response)\n\n\ndef make_request_options(\n        *,\n        query: Query | None = None,\n        extra_headers: Headers | None = None,\n        extra_query: Query | None = None,\n        extra_body: Body | None = None,\n        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,\n        post_parser: PostParser | NotGiven = NOT_GIVEN,\n) -> UserRequestInput:\n    \"\"\"Create a dict of type RequestOptions without keys of NotGiven values.\"\"\"\n    options: UserRequestInput = {}\n    if extra_headers is not None:\n        options[\"headers\"] = extra_headers\n\n    if extra_body is not None:\n        options[\"extra_json\"] = cast(AnyMapping, extra_body)\n\n    if query is not None:\n        options[\"params\"] = query\n\n    if extra_query is not None:\n        options[\"params\"] = {**options.get(\"params\", {}), **extra_query}\n\n    if not isinstance(timeout, NotGiven):\n        options[\"timeout\"] = timeout\n\n    if is_given(post_parser):\n        # internal\n        options[\"post_parser\"] = post_parser  # type: ignore\n\n    return options\n\n\ndef _merge_mappings(\n        obj1: Mapping[_T_co, Union[_T, Omit]],\n        obj2: Mapping[_T_co, Union[_T, Omit]],\n) -> Dict[_T_co, _T]:\n    \"\"\"Merge two mappings of the same type, removing any values that are instances of `Omit`.\n\n    In cases with duplicate keys the second mapping takes precedence.\n    \"\"\"\n    merged = {**obj1, **obj2}\n    return {key: value for key, value in merged.items() if not isinstance(value, Omit)}\n"
  },
  {
    "path": "zhipuai/core/_jwt_token.py",
    "content": "# -*- coding:utf-8 -*-\nimport time\n\nimport cachetools.func\nimport jwt\n\n# 缓存时间 3分钟\nCACHE_TTL_SECONDS = 3 * 60\n\n# token 有效期比缓存时间 多30秒\nAPI_TOKEN_TTL_SECONDS = CACHE_TTL_SECONDS + 30\n\n\n@cachetools.func.ttl_cache(maxsize=10, ttl=CACHE_TTL_SECONDS)\ndef generate_token(apikey: str):\n    try:\n        api_key, secret = apikey.split(\".\")\n    except Exception as e:\n        raise Exception(\"invalid api_key\", e)\n\n    payload = {\n        \"api_key\": api_key,\n        \"exp\": int(round(time.time() * 1000)) + API_TOKEN_TTL_SECONDS * 1000,\n        \"timestamp\": int(round(time.time() * 1000)),\n    }\n    ret = jwt.encode(\n        payload,\n        secret,\n        algorithm=\"HS256\",\n        headers={\"alg\": \"HS256\", \"sign_type\": \"SIGN\"},\n    )\n    return ret\n"
  },
  {
    "path": "zhipuai/core/_legacy_binary_response.py",
    "content": "from __future__ import annotations\n\nimport httpx\nimport os\nfrom typing import Iterator, AsyncIterator, Any\n\n\nclass HttpxResponseContent:\n\n    @property\n    def content(self) -> bytes:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    @property\n    def text(self) -> str:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    @property\n    def encoding(self) -> str | None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    @property\n    def charset_encoding(self) -> str | None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def json(self, **kwargs: Any) -> Any:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def read(self) -> bytes:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def iter_lines(self) -> Iterator[str]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def write_to_file(\n            self,\n            file: str | os.PathLike[str],\n    ) -> None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def stream_to_file(\n            self,\n            file: str | os.PathLike[str],\n            *,\n            chunk_size: int | None = None,\n    ) -> None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    def close(self) -> None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aread(self) -> bytes:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aiter_lines(self) -> AsyncIterator[str]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def astream_to_file(\n            self,\n            file: str | os.PathLike[str],\n            *,\n            chunk_size: int | None = None,\n    ) -> None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n    async def aclose(self) -> None:\n        raise NotImplementedError(\"This method is not implemented for this class.\")\n\n\nclass HttpxBinaryResponseContent(HttpxResponseContent):\n    response: httpx.Response\n\n    def __init__(self, response: httpx.Response) -> None:\n        self.response = response\n\n    @property\n    def content(self) -> bytes:\n        return self.response.content\n\n    @property\n    def encoding(self) -> str | None:\n        return self.response.encoding\n\n    @property\n    def charset_encoding(self) -> str | None:\n        return self.response.charset_encoding\n\n    def read(self) -> bytes:\n        return self.response.read()\n\n    def text(self) -> str:\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    def json(self, **kwargs: Any) -> Any:\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:\n\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    def iter_lines(self) -> Iterator[str]:\n\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    async def aiter_lines(self) -> AsyncIterator[str]:\n        raise NotImplementedError(\"Not implemented for binary response content\")\n\n    def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:\n        return self.response.iter_bytes(chunk_size)\n\n    def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]:\n        return self.response.iter_raw(chunk_size)\n\n    def write_to_file(\n            self,\n            file: str | os.PathLike[str],\n    ) -> None:\n        \"\"\"Write the output to the given file.\n\n        Accepts a filename or any path-like object, e.g. pathlib.Path\n\n        Note: if you want to stream the data to the file instead of writing\n        all at once then you should use `.with_streaming_response` when making\n        the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')`\n        \"\"\"\n        with open(file, mode=\"wb\") as f:\n            for data in self.response.iter_bytes():\n                f.write(data)\n\n    def stream_to_file(\n            self,\n            file: str | os.PathLike[str],\n            *,\n            chunk_size: int | None = None,\n    ) -> None:\n        with open(file, mode=\"wb\") as f:\n            for data in self.response.iter_bytes(chunk_size):\n                f.write(data)\n\n    def close(self) -> None:\n        return self.response.close()\n\n    async def aread(self) -> bytes:\n        return await self.response.aread()\n\n    async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:\n        return self.response.aiter_bytes(chunk_size)\n\n    async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:\n        return self.response.aiter_raw(chunk_size)\n\n    async def astream_to_file(\n            self,\n            file: str | os.PathLike[str],\n            *,\n            chunk_size: int | None = None,\n    ) -> None:\n        path = anyio.Path(file)\n        async with await path.open(mode=\"wb\") as f:\n            async for data in self.response.aiter_bytes(chunk_size):\n                await f.write(data)\n\n    async def aclose(self) -> None:\n        return await self.response.aclose()\n\n\nclass HttpxTextBinaryResponseContent(HttpxBinaryResponseContent):\n    response: httpx.Response\n\n    @property\n    def text(self) -> str:\n        return self.response.text\n\n    def json(self, **kwargs: Any) -> Any:\n        return self.response.json(**kwargs)\n\n    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:\n        return self.response.iter_text(chunk_size)\n\n    def iter_lines(self) -> Iterator[str]:\n        return self.response.iter_lines()\n\n    async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:\n        return self.response.aiter_text(chunk_size)\n\n    async def aiter_lines(self) -> AsyncIterator[str]:\n        return self.response.aiter_lines()\n"
  },
  {
    "path": "zhipuai/core/_legacy_response.py",
    "content": "from __future__ import annotations\n\nimport os\nimport inspect\nimport logging\nimport datetime\nimport functools\nfrom collections.abc import Iterator\nfrom typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload, AsyncIterator\nfrom typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin\n\nimport anyio\nimport httpx\nimport pydantic\n\nfrom ._base_type import NoneType\nfrom ._utils import is_given, extract_type_arg, is_annotated_type\nfrom ._base_models import BaseModel, is_basemodel\nfrom ._constants import RAW_RESPONSE_HEADER\nfrom ._sse_client import StreamResponse, is_stream_class_type, extract_stream_chunk_type\nfrom ._errors import APIResponseValidationError\nfrom ._legacy_binary_response import HttpxBinaryResponseContent, HttpxTextBinaryResponseContent, HttpxResponseContent\n\nif TYPE_CHECKING:\n    from ._request_opt import FinalRequestOptions\n    from ._http_client import HttpClient\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\n_T = TypeVar(\"_T\")\n\nlog: logging.Logger = logging.getLogger(__name__)\n\n\nclass LegacyAPIResponse(Generic[R]):\n    \"\"\"This is a legacy class as it will be replaced by `APIResponse`\n    and `AsyncAPIResponse` in the `_response.py` file in the next major\n    release.\n\n    For the sync client this will mostly be the same with the exception\n    of `content` & `text` will be methods instead of properties. In the\n    async client, all methods will be async.\n\n    A migration script will be provided & the migration in general should\n    be smooth.\n    \"\"\"\n\n    _cast_type: type[R]\n    _client: \"HttpClient\"\n    _parsed_by_type: dict[type[Any], Any]\n    _stream: bool\n    _stream_cls: type[StreamResponse[Any]] | None\n    _options: FinalRequestOptions\n\n    http_response: httpx.Response\n\n    def __init__(\n            self,\n            *,\n            raw: httpx.Response,\n            cast_type: type[R],\n            client: \"HttpClient\",\n            stream: bool,\n            stream_cls: type[StreamResponse[Any]] | None,\n            options: FinalRequestOptions,\n    ) -> None:\n        self._cast_type = cast_type\n        self._client = client\n        self._parsed_by_type = {}\n        self._stream = stream\n        self._stream_cls = stream_cls\n        self._options = options\n        self.http_response = raw\n\n    @property\n    def request_id(self) -> str | None:\n        return self.http_response.headers.get(\"x-request-id\")  # type: ignore[no-any-return]\n\n    @overload\n    def parse(self, *, to: type[_T]) -> _T:\n        ...\n\n    @overload\n    def parse(self) -> R:\n        ...\n\n    def parse(self, *, to: type[_T] | None = None) -> R | _T:\n        \"\"\"Returns the rich python representation of this response's data.\n\n        NOTE: For the async client: this will become a coroutine in the next major version.\n\n        For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`.\n\n        You can customise the type that the response is parsed into through\n        the `to` argument, e.g.\n\n        ```py\n        from zhipuai import BaseModel\n\n\n        class MyModel(BaseModel):\n            foo: str\n\n\n        obj = response.parse(to=MyModel)\n        print(obj.foo)\n        ```\n\n        We support parsing:\n          - `BaseModel`\n          - `dict`\n          - `list`\n          - `Union`\n          - `str`\n          - `int`\n          - `float`\n          - `httpx.Response`\n        \"\"\"\n        cache_key = to if to is not None else self._cast_type\n        cached = self._parsed_by_type.get(cache_key)\n        if cached is not None:\n            return cached  # type: ignore[no-any-return]\n\n        parsed = self._parse(to=to)\n        if is_given(self._options.post_parser):\n            parsed = self._options.post_parser(parsed)\n\n        self._parsed_by_type[cache_key] = parsed\n        return parsed\n\n    @property\n    def headers(self) -> httpx.Headers:\n        return self.http_response.headers\n\n    @property\n    def http_request(self) -> httpx.Request:\n        return self.http_response.request\n\n    @property\n    def status_code(self) -> int:\n        return self.http_response.status_code\n\n    @property\n    def url(self) -> httpx.URL:\n        return self.http_response.url\n\n    @property\n    def method(self) -> str:\n        return self.http_request.method\n\n    @property\n    def content(self) -> bytes:\n        \"\"\"Return the binary response content.\n\n        NOTE: this will be removed in favour of `.read()` in the\n        next major version.\n        \"\"\"\n        return self.http_response.content\n\n    @property\n    def text(self) -> str:\n        \"\"\"Return the decoded response content.\n\n        NOTE: this will be turned into a method in the next major version.\n        \"\"\"\n        return self.http_response.text\n\n    @property\n    def http_version(self) -> str:\n        return self.http_response.http_version\n\n    @property\n    def is_closed(self) -> bool:\n        return self.http_response.is_closed\n\n    @property\n    def elapsed(self) -> datetime.timedelta:\n        \"\"\"The time taken for the complete request/response cycle to complete.\"\"\"\n        return self.http_response.elapsed\n\n    def _parse(self, *, to: type[_T] | None = None) -> R | _T:\n        # unwrap `Annotated[T, ...]` -> `T`\n        if to and is_annotated_type(to):\n            to = extract_type_arg(to, 0)\n\n        if self._stream:\n            if to:\n                if not is_stream_class_type(to):\n                    raise TypeError(f\"Expected custom parse type to be a subclass of {StreamResponse}\")\n\n                return cast(\n                    _T,\n                    to(\n                        cast_type=extract_stream_chunk_type(\n                            to,\n                            failure_message=\"Expected custom stream type to be passed with a type argument, e.g. StreamResponse[ChunkType]\",\n                        ),\n                        response=self.http_response,\n                        client=cast(Any, self._client),\n                    ),\n                )\n\n            if self._stream_cls:\n                return cast(\n                    R,\n                    self._stream_cls(\n                        cast_type=extract_stream_chunk_type(self._stream_cls),\n                        response=self.http_response,\n                        client=cast(Any, self._client),\n                    ),\n                )\n\n            stream_cls = cast(\"type[StreamResponse[Any]] | None\", self._client._default_stream_cls)\n            if stream_cls is None:\n                raise MissingStreamClassError()\n\n            return cast(\n                R,\n                stream_cls(\n                    cast_type=self._cast_type,\n                    response=self.http_response,\n                    client=cast(Any, self._client),\n                ),\n            )\n\n        cast_type = to if to is not None else self._cast_type\n\n        # unwrap `Annotated[T, ...]` -> `T`\n        if is_annotated_type(cast_type):\n            cast_type = extract_type_arg(cast_type, 0)\n\n        if cast_type is NoneType:\n            return cast(R, None)\n\n        response = self.http_response\n        if cast_type == str:\n            return cast(R, response.text)\n\n        if cast_type == int:\n            return cast(R, int(response.text))\n\n        if cast_type == float:\n            return cast(R, float(response.text))\n\n        origin = get_origin(cast_type) or cast_type\n\n        if inspect.isclass(origin) and issubclass(origin, HttpxResponseContent):\n\n            # in the response, e.g. mime file\n            *_, filename = response.headers.get(\"content-disposition\", \"\").split(\"filename=\")\n            # 判断文件类型是jsonl类型的使用HttpxTextBinaryResponseContent\n            if filename and filename.endswith(\".jsonl\"):\n                return cast(R, HttpxTextBinaryResponseContent(response))\n            elif filename and filename.endswith(\".xlsx\"):\n                return cast(R, HttpxTextBinaryResponseContent(response))\n            else:\n                return cast(R, cast_type(response))  # type: ignore\n\n        if origin == LegacyAPIResponse:\n            raise RuntimeError(\"Unexpected state - cast_type is `APIResponse`\")\n\n        if inspect.isclass(origin) and issubclass(origin, httpx.Response):\n            # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response\n            # and pass that class to our request functions. We cannot change the variance to be either\n            # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct\n            # the response class ourselves but that is something that should be supported directly in httpx\n            # as it would be easy to incorrectly construct the Response object due to the multitude of arguments.\n            if cast_type != httpx.Response:\n                raise ValueError(f\"Subclasses of httpx.Response cannot be passed to `cast_type`\")\n            return cast(R, response)\n\n        if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel):\n            raise TypeError(\"Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`\")\n\n        if (\n                cast_type is not object\n                and not origin is list\n                and not origin is dict\n                and not origin is Union\n                and not issubclass(origin, BaseModel)\n        ):\n            raise RuntimeError(\n                f\"Unsupported type, expected {cast_type} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}.\"\n            )\n\n        # split is required to handle cases where additional information is included\n        # in the response, e.g. application/json; charset=utf-8\n        content_type, *_ = response.headers.get(\"content-type\", \"*\").split(\";\")\n        if content_type != \"application/json\":\n            if is_basemodel(cast_type):\n                try:\n                    data = response.json()\n                except Exception as exc:\n                    log.debug(\"Could not read JSON from response data due to %s - %s\", type(exc), exc)\n                else:\n                    return self._client._process_response_data(\n                        data=data,\n                        cast_type=cast_type,  # type: ignore\n                        response=response,\n                    )\n\n            if self._client._strict_response_validation:\n                raise APIResponseValidationError(\n                    response=response,\n                    message=f\"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.\",\n                    json_data=response.text,\n                )\n\n            # If the API responds with content that isn't JSON then we just return\n            # the (decoded) text without performing any parsing so that you can still\n            # handle the response however you need to.\n            return response.text  # type: ignore\n\n        data = response.json()\n\n        return self._client._process_response_data(\n            data=data,\n            cast_type=cast_type,  # type: ignore\n            response=response,\n        )\n\n    @override\n    def __repr__(self) -> str:\n        return f\"<APIResponse [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_type}>\"\n\n\nclass MissingStreamClassError(TypeError):\n    def __init__(self) -> None:\n        super().__init__(\n            \"The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference\",\n        )\n\n\ndef to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]:\n    \"\"\"Higher order function that takes one of our bound API methods and wraps it\n    to support returning the raw `APIResponse` object directly.\n    \"\"\"\n\n    @functools.wraps(func)\n    def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]:\n        extra_headers: dict[str, str] = {**(cast(Any, kwargs.get(\"extra_headers\")) or {})}\n        extra_headers[RAW_RESPONSE_HEADER] = \"true\"\n\n        kwargs[\"extra_headers\"] = extra_headers\n\n        return cast(LegacyAPIResponse[R], func(*args, **kwargs))\n\n    return wrapped\n\n"
  },
  {
    "path": "zhipuai/core/_request_opt.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union, Any, cast, TYPE_CHECKING\n\nfrom ._constants import RAW_RESPONSE_HEADER\nfrom ._utils import is_given\nfrom ._base_compat import ConfigDict, PYDANTIC_V2\nimport pydantic.generics\nfrom httpx import Timeout\nfrom typing_extensions import (\n    final, Unpack, ClassVar, TypedDict, Required, Callable\n\n)\n\nfrom ._base_type import Body, NotGiven, Headers, HttpxRequestFiles, Query, AnyMapping\nfrom ._utils import remove_notgiven_indict, strip_not_given\n\n\nclass UserRequestInput(TypedDict, total=False):\n    headers: Headers\n    max_retries: int\n    timeout: float | Timeout | None\n    params: Query\n    extra_json: AnyMapping\n\n\nclass FinalRequestOptionsInput(TypedDict, total=False):\n    method: Required[str]\n    url: Required[str]\n    params: Query\n    headers: Headers\n    max_retries: int\n    timeout: float | Timeout | None\n    files: HttpxRequestFiles | None\n    json_data: Body\n    extra_json: AnyMapping\n\n\n@final\nclass FinalRequestOptions(pydantic.BaseModel):\n    method: str\n    url: str\n    params: Query = {}\n    headers: Union[Headers, NotGiven] = NotGiven()\n    max_retries: Union[int, NotGiven] = NotGiven()\n    timeout: Union[float, Timeout, None, NotGiven] = NotGiven()\n    files: Union[HttpxRequestFiles, None] = None\n    idempotency_key: Union[str, None] = None\n    post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()\n\n    # It should be noted that we cannot use `json` here as that would override\n    # a BaseModel method in an incompatible fashion.\n    json_data: Union[Body, None] = None\n    extra_json: Union[AnyMapping, None] = None\n\n    if PYDANTIC_V2:\n        model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)\n    else:\n\n        class Config(pydantic.BaseConfig):  # pyright: ignore[reportDeprecated]\n            arbitrary_types_allowed: bool = True\n\n    def get_max_retries(self, max_retries: int) -> int:\n        if isinstance(self.max_retries, NotGiven):\n            return max_retries\n        return self.max_retries\n\n    def _strip_raw_response_header(self) -> None:\n        if not is_given(self.headers):\n            return\n\n        if self.headers.get(RAW_RESPONSE_HEADER):\n            self.headers = {**self.headers}\n            self.headers.pop(RAW_RESPONSE_HEADER)\n\n    # override the `construct` method so that we can run custom transformations.\n    # this is necessary as we don't want to do any actual runtime type checking\n    # (which means we can't use validators) but we do want to ensure that `NotGiven`\n    # values are not present\n    #\n    # type ignore required because we're adding explicit types to `**values`\n    @classmethod\n    def construct(  # type: ignore\n            cls,\n            _fields_set: set[str] | None = None,\n            **values: Unpack[UserRequestInput],\n    ) -> FinalRequestOptions:\n        kwargs: dict[str, Any] = {\n            # we unconditionally call `strip_not_given` on any value\n            # as it will just ignore any non-mapping types\n            key: strip_not_given(value)\n            for key, value in values.items()\n        }\n        if PYDANTIC_V2:\n            return super().model_construct(_fields_set, **kwargs)\n        return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs))  # pyright: ignore[reportDeprecated]\n\n    if not TYPE_CHECKING:\n        # type checkers incorrectly complain about this assignment\n        model_construct = construct\n"
  },
  {
    "path": "zhipuai/core/_response.py",
    "content": "from __future__ import annotations\n\nimport datetime\nimport inspect\nfrom typing import TypeVar, Generic, cast, Any, TYPE_CHECKING, Iterator, Union, overload\n\nimport httpx\nimport logging\nimport pydantic\nfrom typing_extensions import ParamSpec, get_origin, get_args, override\n\nfrom ._base_type import NoneType\nfrom ._sse_client import StreamResponse, is_stream_class_type, extract_stream_chunk_type\nfrom ._base_models import is_basemodel, BaseModel\nfrom ._utils import is_annotated_type, extract_type_arg, extract_type_var_from_base, is_given\nfrom ._errors import ZhipuAIError, APIResponseValidationError\n\nif TYPE_CHECKING:\n    from ._request_opt import FinalRequestOptions\n    from ._http_client import HttpClient\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\n_T = TypeVar(\"_T\")\n_APIResponseT = TypeVar(\"_APIResponseT\", bound=\"APIResponse[Any]\")\nlog: logging.Logger = logging.getLogger(__name__)\n\n\nclass BaseAPIResponse(Generic[R]):\n    _cast_type: type[R]\n    _client: \"HttpClient\"\n    _parsed_by_type: dict[type[Any], Any]\n    _is_sse_stream: bool\n    _stream_cls: type[StreamResponse[Any]]\n    _options: FinalRequestOptions\n    http_response: httpx.Response\n\n    def __init__(\n            self,\n            *,\n            raw: httpx.Response,\n            cast_type: type[R],\n            client: \"HttpClient\",\n            stream: bool,\n            stream_cls: type[StreamResponse[Any]] | None = None,\n            options: FinalRequestOptions,\n    ) -> None:\n        self._cast_type = cast_type\n        self._client = client\n        self._parsed_by_type = {}\n        self._is_sse_stream = stream\n        self._stream_cls = stream_cls\n        self._options = options\n        self.http_response = raw\n\n    def _parse(self, *, to: type[_T] | None = None) -> R | _T:\n        # unwrap `Annotated[T, ...]` -> `T`\n        if to and is_annotated_type(to):\n            to = extract_type_arg(to, 0)\n\n        if self._is_sse_stream:\n            if to:\n                if not is_stream_class_type(to):\n                    raise TypeError(f\"Expected custom parse type to be a subclass of {StreamResponse}\")\n\n                return cast(\n                    _T,\n                    to(\n                        cast_type=extract_stream_chunk_type(\n                            to,\n                            failure_message=\"Expected custom stream type to be passed with a type argument, e.g. StreamResponse[ChunkType]\",\n                        ),\n                        response=self.http_response,\n                        client=cast(Any, self._client),\n                    ),\n                )\n\n            if self._stream_cls:\n                return cast(\n                    R,\n                    self._stream_cls(\n                        cast_type=extract_stream_chunk_type(self._stream_cls),\n                        response=self.http_response,\n                        client=cast(Any, self._client),\n                    ),\n                )\n\n            stream_cls = cast(\"type[Stream[Any]] | None\", self._client._default_stream_cls)\n            if stream_cls is None:\n                raise MissingStreamClassError()\n\n            return cast(\n                R,\n                stream_cls(\n                    cast_type=self._cast_type,\n                    response=self.http_response,\n                    client=cast(Any, self._client),\n                ),\n            )\n\n        cast_type = to if to is not None else self._cast_type\n\n        # unwrap `Annotated[T, ...]` -> `T`\n        if is_annotated_type(cast_type):\n            cast_type = extract_type_arg(cast_type, 0)\n\n        if cast_type is NoneType:\n            return cast(R, None)\n\n        response = self.http_response\n        if cast_type == str:\n            return cast(R, response.text)\n\n        if cast_type == bytes:\n            return cast(R, response.content)\n\n        if cast_type == int:\n            return cast(R, int(response.text))\n\n        if cast_type == float:\n            return cast(R, float(response.text))\n\n        origin = get_origin(cast_type) or cast_type\n\n        # handle the legacy binary response case\n        if inspect.isclass(cast_type) and cast_type.__name__ == \"HttpxBinaryResponseContent\":\n            return cast(R, cast_type(response))  # type: ignore\n\n        if origin == APIResponse:\n            raise RuntimeError(\"Unexpected state - cast_type is `APIResponse`\")\n\n        if inspect.isclass(origin) and issubclass(origin, httpx.Response):\n            # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response\n            # and pass that class to our request functions. We cannot change the variance to be either\n            # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct\n            # the response class ourselves but that is something that should be supported directly in httpx\n            # as it would be easy to incorrectly construct the Response object due to the multitude of arguments.\n            if cast_type != httpx.Response:\n                raise ValueError(f\"Subclasses of httpx.Response cannot be passed to `cast_type`\")\n            return cast(R, response)\n\n        if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel):\n            raise TypeError(\"Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`\")\n\n        if (\n                cast_type is not object\n                and not origin is list\n                and not origin is dict\n                and not origin is Union\n                and not issubclass(origin, BaseModel)\n        ):\n            raise RuntimeError(\n                f\"Unsupported type, expected {cast_type} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}.\"\n            )\n\n        # split is required to handle cases where additional information is included\n        # in the response, e.g. application/json; charset=utf-8\n        content_type, *_ = response.headers.get(\"content-type\", \"*\").split(\";\")\n        if content_type != \"application/json\":\n            if is_basemodel(cast_type):\n                try:\n                    data = response.json()\n                except Exception as exc:\n                    log.debug(\"Could not read JSON from response data due to %s - %s\", type(exc), exc)\n                else:\n                    return self._client._process_response_data(\n                        data=data,\n                        cast_type=cast_type,  # type: ignore\n                        response=response,\n                    )\n\n            if self._client._strict_response_validation:\n                raise APIResponseValidationError(\n                    response=response,\n                    message=f\"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.\",\n                    json_data=response.text,\n                )\n\n            # If the API responds with content that isn't JSON then we just return\n            # the (decoded) text without performing any parsing so that you can still\n            # handle the response however you need to.\n            return response.text  # type: ignore\n\n        data = response.json()\n\n        return self._client._process_response_data(\n            data=data,\n            cast_type=cast_type,  # type: ignore\n            response=response,\n        )\n\n    @property\n    def headers(self) -> httpx.Headers:\n        return self.http_response.headers\n\n    @property\n    def http_request(self) -> httpx.Request:\n        \"\"\"Returns the httpx Request instance associated with the current response.\"\"\"\n        return self.http_response.request\n\n    @property\n    def status_code(self) -> int:\n        return self.http_response.status_code\n\n    @property\n    def url(self) -> httpx.URL:\n        \"\"\"Returns the URL for which the request was made.\"\"\"\n        return self.http_response.url\n\n    @property\n    def method(self) -> str:\n        return self.http_request.method\n\n    @property\n    def http_version(self) -> str:\n        return self.http_response.http_version\n\n    @property\n    def elapsed(self) -> datetime.timedelta:\n        \"\"\"The time taken for the complete request/response cycle to complete.\"\"\"\n        return self.http_response.elapsed\n\n    @property\n    def is_closed(self) -> bool:\n        \"\"\"Whether or not the response body has been closed.\n\n        If this is False then there is response data that has not been read yet.\n        You must either fully consume the response body or call `.close()`\n        before discarding the response to prevent resource leaks.\n        \"\"\"\n        return self.http_response.is_closed\n\n    @override\n    def __repr__(self) -> str:\n        return (\n            f\"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_type}>\"\n        )\n\n\nclass APIResponse(BaseAPIResponse[R]):\n\n    @property\n    def request_id(self) -> str | None:\n        return self.http_response.headers.get(\"x-request-id\")  # type: ignore[no-any-return]\n\n    @overload\n    def parse(self, *, to: type[_T]) -> _T:\n        ...\n\n    @overload\n    def parse(self) -> R:\n        ...\n\n    def parse(self, *, to: type[_T] | None = None) -> R | _T:\n        \"\"\"Returns the rich python representation of this response's data.\n\n        For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`.\n\n        You can customise the type that the response is parsed into through\n        the `to` argument, e.g.\n\n        ```py\n        from openai import BaseModel\n\n\n        class MyModel(BaseModel):\n            foo: str\n\n\n        obj = response.parse(to=MyModel)\n        print(obj.foo)\n        ```\n\n        We support parsing:\n          - `BaseModel`\n          - `dict`\n          - `list`\n          - `Union`\n          - `str`\n          - `int`\n          - `float`\n          - `httpx.Response`\n        \"\"\"\n        cache_key = to if to is not None else self._cast_type\n        cached = self._parsed_by_type.get(cache_key)\n        if cached is not None:\n            return cached  # type: ignore[no-any-return]\n\n        if not self._is_sse_stream:\n            self.read()\n\n        parsed = self._parse(to=to)\n        if is_given(self._options.post_parser):\n            parsed = self._options.post_parser(parsed)\n\n        self._parsed_by_type[cache_key] = parsed\n        return parsed\n\n    def read(self) -> bytes:\n        \"\"\"Read and return the binary response content.\"\"\"\n        try:\n            return self.http_response.read()\n        except httpx.StreamConsumed as exc:\n            # The default error raised by httpx isn't very\n            # helpful in our case so we re-raise it with\n            # a different error message.\n            raise StreamAlreadyConsumed() from exc\n\n    def text(self) -> str:\n        \"\"\"Read and decode the response content into a string.\"\"\"\n        self.read()\n        return self.http_response.text\n\n    def json(self) -> object:\n        \"\"\"Read and decode the JSON response content.\"\"\"\n        self.read()\n        return self.http_response.json()\n\n    def close(self) -> None:\n        \"\"\"Close the response and release the connection.\n\n        Automatically called if the response body is read to completion.\n        \"\"\"\n        self.http_response.close()\n\n    def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:\n        \"\"\"\n        A byte-iterator over the decoded response content.\n\n        This automatically handles gzip, deflate and brotli encoded responses.\n        \"\"\"\n        for chunk in self.http_response.iter_bytes(chunk_size):\n            yield chunk\n\n    def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:\n        \"\"\"A str-iterator over the decoded response content\n        that handles both gzip, deflate, etc but also detects the content's\n        string encoding.\n        \"\"\"\n        for chunk in self.http_response.iter_text(chunk_size):\n            yield chunk\n\n    def iter_lines(self) -> Iterator[str]:\n        \"\"\"Like `iter_text()` but will only yield chunks for each line\"\"\"\n        for chunk in self.http_response.iter_lines():\n            yield chunk\n\n\nclass MissingStreamClassError(TypeError):\n    def __init__(self) -> None:\n        super().__init__(\n            \"The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference\",\n        )\n\n\nclass StreamAlreadyConsumed(ZhipuAIError):\n    \"\"\"\n    Attempted to read or stream content, but the content has already\n    been streamed.\n\n    This can happen if you use a method like `.iter_lines()` and then attempt\n    to read th entire response body afterwards, e.g.\n\n    ```py\n    response = await client.post(...)\n    async for line in response.iter_lines():\n        ...  # do something with `line`\n\n    content = await response.read()\n    # ^ error\n    ```\n\n    If you want this behaviour you'll need to either manually accumulate the response\n    content or call `await response.read()` before iterating over the stream.\n    \"\"\"\n\n    def __init__(self) -> None:\n        message = (\n            \"Attempted to read or stream some content, but the content has \"\n            \"already been streamed. \"\n            \"This could be due to attempting to stream the response \"\n            \"content more than once.\"\n            \"\\n\\n\"\n            \"You can fix this by manually accumulating the response content while streaming \"\n            \"or by calling `.read()` before starting to stream.\"\n        )\n        super().__init__(message)\n\n\ndef extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type:\n    \"\"\"Given a type like `APIResponse[T]`, returns the generic type variable `T`.\n\n    This also handles the case where a concrete subclass is given, e.g.\n    ```py\n    class MyResponse(APIResponse[bytes]):\n        ...\n\n    extract_response_type(MyResponse) -> bytes\n    ```\n    \"\"\"\n    return extract_type_var_from_base(\n        typ,\n        generic_bases=cast(\"tuple[type, ...]\", (BaseAPIResponse, APIResponse)),\n        index=0,\n    )\n"
  },
  {
    "path": "zhipuai/core/_sse_client.py",
    "content": "# -*- coding:utf-8 -*-\nfrom __future__ import annotations\n\nimport inspect\nimport json\nfrom typing import TYPE_CHECKING, Generic, Iterator, Mapping, Type, cast\n\nimport httpx\nfrom typing_extensions import TypeGuard\n\nfrom . import get_origin\nfrom ._base_type import ResponseT\nfrom ._errors import APIResponseError\nfrom ._utils import extract_type_var_from_base, is_mapping\n\n_FIELD_SEPARATOR = ':'\n\nif TYPE_CHECKING:\n\tfrom ._http_client import HttpClient\n\n\nclass StreamResponse(Generic[ResponseT]):\n\tresponse: httpx.Response\n\t_cast_type: Type[ResponseT]\n\n\tdef __init__(\n\t\tself,\n\t\t*,\n\t\tcast_type: Type[ResponseT],\n\t\tresponse: httpx.Response,\n\t\tclient: HttpClient,\n\t) -> None:\n\t\tself.response = response\n\t\tself._cast_type = cast_type\n\t\tself._data_process_func = client._process_response_data\n\t\tself._stream_chunks = self.__stream__()\n\n\tdef __next__(self) -> ResponseT:\n\t\treturn self._stream_chunks.__next__()\n\n\tdef __iter__(self) -> Iterator[ResponseT]:\n\t\tfor item in self._stream_chunks:\n\t\t\tyield item\n\n\tdef __stream__(self) -> Iterator[ResponseT]:\n\t\tsse_line_parser = SSELineParser()\n\t\titerator = sse_line_parser.iter_lines(self.response.iter_lines())\n\n\t\tfor sse in iterator:\n\t\t\tif sse.data.startswith('[DONE]'):\n\t\t\t\tbreak\n\t\t\tif sse.event is None:\n\t\t\t\tdata = sse.json_data()\n\t\t\t\tif isinstance(data, Mapping) and data.get('agent_id'):\n\t\t\t\t\tyield self._data_process_func(\n\t\t\t\t\t\tdata=data,\n\t\t\t\t\t\tcast_type=self._cast_type,\n\t\t\t\t\t\tresponse=self.response,\n\t\t\t\t\t)\n\t\t\t\t\tcontinue\n\t\t\t\tif isinstance(data, Mapping) and data.get('error'):\n\t\t\t\t\traise APIResponseError(\n\t\t\t\t\t\tmessage='An error occurred during streaming',\n\t\t\t\t\t\trequest=self.response.request,\n\t\t\t\t\t\tjson_data=data['error'],\n\t\t\t\t\t)\n\t\t\tif sse.event is None:\n\t\t\t\tdata = sse.json_data()\n\t\t\t\tif is_mapping(data) and data.get('error'):\n\t\t\t\t\tmessage = None\n\t\t\t\t\terror = data.get('error')\n\t\t\t\t\tif is_mapping(error):\n\t\t\t\t\t\tmessage = error.get('message')\n\t\t\t\t\tif not message or not isinstance(message, str):\n\t\t\t\t\t\tmessage = 'An error occurred during streaming'\n\n\t\t\t\t\traise APIResponseError(\n\t\t\t\t\t\tmessage=message,\n\t\t\t\t\t\trequest=self.response.request,\n\t\t\t\t\t\tjson_data=data['error'],\n\t\t\t\t\t)\n\t\t\t\tyield self._data_process_func(\n\t\t\t\t\tdata=data, cast_type=self._cast_type, response=self.response\n\t\t\t\t)\n\n\t\t\telse:\n\t\t\t\tdata = sse.json_data()\n\n\t\t\t\tif (\n\t\t\t\t\tsse.event == 'error'\n\t\t\t\t\tand is_mapping(data)\n\t\t\t\t\tand data.get('error')\n\t\t\t\t):\n\t\t\t\t\tmessage = None\n\t\t\t\t\terror = data.get('error')\n\t\t\t\t\tif is_mapping(error):\n\t\t\t\t\t\tmessage = error.get('message')\n\t\t\t\t\tif not message or not isinstance(message, str):\n\t\t\t\t\t\tmessage = 'An error occurred during streaming'\n\n\t\t\t\t\traise APIResponseError(\n\t\t\t\t\t\tmessage=message,\n\t\t\t\t\t\trequest=self.response.request,\n\t\t\t\t\t\tjson_data=data['error'],\n\t\t\t\t\t)\n\t\t\t\tyield self._data_process_func(\n\t\t\t\t\tdata=data, cast_type=self._cast_type, response=self.response\n\t\t\t\t)\n\n\t\tfor sse in iterator:\n\t\t\tpass\n\n\nclass Event(object):\n\tdef __init__(\n\t\tself,\n\t\tevent: str | None = None,\n\t\tdata: str | None = None,\n\t\tid: str | None = None,\n\t\tretry: int | None = None,\n\t):\n\t\tself._event = event\n\t\tself._data = data\n\t\tself._id = id\n\t\tself._retry = retry\n\n\tdef __repr__(self):\n\t\tdata_len = len(self._data) if self._data else 0\n\t\treturn f'Event(event={self._event}, data={self._data} ,data_length={data_len}, id={self._id}, retry={self._retry}'\n\n\t@property\n\tdef event(self):\n\t\treturn self._event\n\n\t@property\n\tdef data(self):\n\t\treturn self._data\n\n\tdef json_data(self):\n\t\treturn json.loads(self._data)\n\n\t@property\n\tdef id(self):\n\t\treturn self._id\n\n\t@property\n\tdef retry(self):\n\t\treturn self._retry\n\n\nclass SSELineParser:\n\t_data: list[str]\n\t_event: str | None\n\t_retry: int | None\n\t_id: str | None\n\n\tdef __init__(self):\n\t\tself._event = None\n\t\tself._data = []\n\t\tself._id = None\n\t\tself._retry = None\n\n\tdef iter_lines(self, lines: Iterator[str]) -> Iterator[Event]:\n\t\tfor line in lines:\n\t\t\tline = line.rstrip('\\n')\n\t\t\tif not line:\n\t\t\t\tif (\n\t\t\t\t\tself._event is None\n\t\t\t\t\tand not self._data\n\t\t\t\t\tand self._id is None\n\t\t\t\t\tand self._retry is None\n\t\t\t\t):\n\t\t\t\t\tcontinue\n\t\t\t\tsse_event = Event(\n\t\t\t\t\tevent=self._event,\n\t\t\t\t\tdata='\\n'.join(self._data),\n\t\t\t\t\tid=self._id,\n\t\t\t\t\tretry=self._retry,\n\t\t\t\t)\n\t\t\t\tself._event = None\n\t\t\t\tself._data = []\n\t\t\t\tself._id = None\n\t\t\t\tself._retry = None\n\n\t\t\t\tyield sse_event\n\t\t\tself.decode_line(line)\n\n\tdef decode_line(self, line: str):\n\t\tif line.startswith(':') or not line:\n\t\t\treturn\n\n\t\tfield, _p, value = line.partition(':')\n\n\t\tif value.startswith(' '):\n\t\t\tvalue = value[1:]\n\t\tif field == 'data':\n\t\t\tself._data.append(value)\n\t\telif field == 'event':\n\t\t\tself._event = value\n\t\telif field == 'retry':\n\t\t\ttry:\n\t\t\t\tself._retry = int(value)\n\t\t\texcept (TypeError, ValueError):\n\t\t\t\tpass\n\t\treturn\n\n\ndef is_stream_class_type(typ: type) -> TypeGuard[type[StreamResponse[object]]]:\n\t\"\"\"TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`\"\"\"\n\torigin = get_origin(typ) or typ\n\treturn inspect.isclass(origin) and issubclass(origin, StreamResponse)\n\n\ndef extract_stream_chunk_type(\n\tstream_cls: type,\n\t*,\n\tfailure_message: str | None = None,\n) -> type:\n\t\"\"\"Given a type like `StreamResponse[T]`, returns the generic type variable `T`.\n\n\tThis also handles the case where a concrete subclass is given, e.g.\n\t```py\n\tclass MyStream(StreamResponse[bytes]):\n\t    ...\n\n\textract_stream_chunk_type(MyStream) -> bytes\n\t```\n\t\"\"\"\n\n\treturn extract_type_var_from_base(\n\t\tstream_cls,\n\t\tindex=0,\n\t\tgeneric_bases=cast('tuple[type, ...]', (StreamResponse,)),\n\t\tfailure_message=failure_message,\n\t)\n"
  },
  {
    "path": "zhipuai/core/_utils/__init__.py",
    "content": "\nfrom ._utils import (\n    remove_notgiven_indict as remove_notgiven_indict,\n    flatten as flatten,\n    is_dict as is_dict,\n    is_list as is_list,\n    is_given as is_given,\n    is_tuple as is_tuple,\n    is_mapping as is_mapping,\n    is_tuple_t as is_tuple_t,\n    parse_date as parse_date,\n    is_iterable as is_iterable,\n    is_sequence as is_sequence,\n    coerce_float as coerce_float,\n    is_mapping_t as is_mapping_t,\n    removeprefix as removeprefix,\n    removesuffix as removesuffix,\n    extract_files as extract_files,\n    is_sequence_t as is_sequence_t,\n    required_args as required_args,\n    coerce_boolean as coerce_boolean,\n    coerce_integer as coerce_integer,\n    file_from_path as file_from_path,\n    parse_datetime as parse_datetime,\n    strip_not_given as strip_not_given,\n    deepcopy_minimal as deepcopy_minimal,\n    get_async_library as get_async_library,\n    maybe_coerce_float as maybe_coerce_float,\n    get_required_header as get_required_header,\n    maybe_coerce_boolean as maybe_coerce_boolean,\n    maybe_coerce_integer as maybe_coerce_integer,\n    drop_prefix_image_data as drop_prefix_image_data,\n)\n\n\nfrom ._typing import (\n    is_list_type as is_list_type,\n    is_union_type as is_union_type,\n    extract_type_arg as extract_type_arg,\n    is_iterable_type as is_iterable_type,\n    is_required_type as is_required_type,\n    is_annotated_type as is_annotated_type,\n    strip_annotated_type as strip_annotated_type,\n    extract_type_var_from_base as extract_type_var_from_base,\n)\n\nfrom ._transform import (\n    PropertyInfo as PropertyInfo,\n    transform as transform,\n    async_transform as async_transform,\n    maybe_transform as maybe_transform,\n    async_maybe_transform as async_maybe_transform,\n)\n"
  },
  {
    "path": "zhipuai/core/_utils/_transform.py",
    "content": "from __future__ import annotations\n\nimport io\nimport base64\nimport pathlib\nfrom typing import Any, Mapping, TypeVar, cast\nfrom datetime import date, datetime\nfrom typing_extensions import Literal, get_args, override, get_type_hints\n\nimport anyio\nimport pydantic\n\nfrom ._utils import (\n    is_list,\n    is_mapping,\n    is_iterable,\n)\nfrom .._files import is_base64_file_input\nfrom ._typing import (\n    is_list_type,\n    is_union_type,\n    extract_type_arg,\n    is_iterable_type,\n    is_required_type,\n    is_annotated_type,\n    strip_annotated_type,\n)\nfrom .._base_compat import model_dump, is_typeddict\n\n_T = TypeVar(\"_T\")\n\n\n# TODO: support for drilling globals() and locals()\n# TODO: ensure works correctly with forward references in all cases\n\n\nPropertyFormat = Literal[\"iso8601\", \"base64\", \"custom\"]\n\n\nclass PropertyInfo:\n    \"\"\"Metadata class to be used in Annotated types to provide information about a given type.\n\n    For example:\n\n    class MyParams(TypedDict):\n        account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')]\n\n    This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API.\n    \"\"\"\n\n    alias: str | None\n    format: PropertyFormat | None\n    format_template: str | None\n    discriminator: str | None\n\n    def __init__(\n            self,\n            *,\n            alias: str | None = None,\n            format: PropertyFormat | None = None,\n            format_template: str | None = None,\n            discriminator: str | None = None,\n    ) -> None:\n        self.alias = alias\n        self.format = format\n        self.format_template = format_template\n        self.discriminator = discriminator\n\n    @override\n    def __repr__(self) -> str:\n        return f\"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')\"\n\n\ndef maybe_transform(\n        data: object,\n        expected_type: object,\n) -> Any | None:\n    \"\"\"Wrapper over `transform()` that allows `None` to be passed.\n\n    See `transform()` for more details.\n    \"\"\"\n    if data is None:\n        return None\n    return transform(data, expected_type)\n\n\n# Wrapper over _transform_recursive providing fake types\ndef transform(\n        data: _T,\n        expected_type: object,\n) -> _T:\n    \"\"\"Transform dictionaries based off of type information from the given type, for example:\n\n    ```py\n    class Params(TypedDict, total=False):\n        card_id: Required[Annotated[str, PropertyInfo(alias=\"cardID\")]]\n\n\n    transformed = transform({\"card_id\": \"<my card ID>\"}, Params)\n    # {'cardID': '<my card ID>'}\n    ```\n\n    Any keys / data that does not have type information given will be included as is.\n\n    It should be noted that the transformations that this function does are not represented in the type system.\n    \"\"\"\n    transformed = _transform_recursive(data, annotation=cast(type, expected_type))\n    return cast(_T, transformed)\n\n\ndef _get_annotated_type(type_: type) -> type | None:\n    \"\"\"If the given type is an `Annotated` type then it is returned, if not `None` is returned.\n\n    This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]`\n    \"\"\"\n    if is_required_type(type_):\n        # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]`\n        type_ = get_args(type_)[0]\n\n    if is_annotated_type(type_):\n        return type_\n\n    return None\n\n\ndef _maybe_transform_key(key: str, type_: type) -> str:\n    \"\"\"Transform the given `data` based on the annotations provided in `type_`.\n\n    Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata.\n    \"\"\"\n    annotated_type = _get_annotated_type(type_)\n    if annotated_type is None:\n        # no `Annotated` definition for this type, no transformation needed\n        return key\n\n    # ignore the first argument as it is the actual type\n    annotations = get_args(annotated_type)[1:]\n    for annotation in annotations:\n        if isinstance(annotation, PropertyInfo) and annotation.alias is not None:\n            return annotation.alias\n\n    return key\n\n\ndef _transform_recursive(\n        data: object,\n        *,\n        annotation: type,\n        inner_type: type | None = None,\n) -> object:\n    \"\"\"Transform the given data against the expected type.\n\n    Args:\n        annotation: The direct type annotation given to the particular piece of data.\n            This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc\n\n        inner_type: If applicable, this is the \"inside\" type. This is useful in certain cases where the outside type\n            is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in\n            the list can be transformed using the metadata from the container type.\n\n            Defaults to the same value as the `annotation` argument.\n    \"\"\"\n    if inner_type is None:\n        inner_type = annotation\n\n    stripped_type = strip_annotated_type(inner_type)\n    if is_typeddict(stripped_type) and is_mapping(data):\n        return _transform_typeddict(data, stripped_type)\n\n    if (\n            # List[T]\n            (is_list_type(stripped_type) and is_list(data))\n            # Iterable[T]\n            or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))\n    ):\n        inner_type = extract_type_arg(stripped_type, 0)\n        return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]\n\n    if is_union_type(stripped_type):\n        # For union types we run the transformation against all subtypes to ensure that everything is transformed.\n        #\n        # TODO: there may be edge cases where the same normalized field name will transform to two different names\n        # in different subtypes.\n        for subtype in get_args(stripped_type):\n            data = _transform_recursive(data, annotation=annotation, inner_type=subtype)\n        return data\n\n    if isinstance(data, pydantic.BaseModel):\n        return model_dump(data, exclude_unset=True)\n\n    annotated_type = _get_annotated_type(annotation)\n    if annotated_type is None:\n        return data\n\n    # ignore the first argument as it is the actual type\n    annotations = get_args(annotated_type)[1:]\n    for annotation in annotations:\n        if isinstance(annotation, PropertyInfo) and annotation.format is not None:\n            return _format_data(data, annotation.format, annotation.format_template)\n\n    return data\n\n\ndef _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:\n    if isinstance(data, (date, datetime)):\n        if format_ == \"iso8601\":\n            return data.isoformat()\n\n        if format_ == \"custom\" and format_template is not None:\n            return data.strftime(format_template)\n\n    if format_ == \"base64\" and is_base64_file_input(data):\n        binary: str | bytes | None = None\n\n        if isinstance(data, pathlib.Path):\n            binary = data.read_bytes()\n        elif isinstance(data, io.IOBase):\n            binary = data.read()\n\n            if isinstance(binary, str):  # type: ignore[unreachable]\n                binary = binary.encode()\n\n        if not isinstance(binary, bytes):\n            raise RuntimeError(f\"Could not read bytes from {data}; Received {type(binary)}\")\n\n        return base64.b64encode(binary).decode(\"ascii\")\n\n    return data\n\n\ndef _transform_typeddict(\n        data: Mapping[str, object],\n        expected_type: type,\n) -> Mapping[str, object]:\n    result: dict[str, object] = {}\n    annotations = get_type_hints(expected_type, include_extras=True)\n    for key, value in data.items():\n        type_ = annotations.get(key)\n        if type_ is None:\n            # we do not have a type annotation for this field, leave it as is\n            result[key] = value\n        else:\n            result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_)\n    return result\n\n\nasync def async_maybe_transform(\n        data: object,\n        expected_type: object,\n) -> Any | None:\n    \"\"\"Wrapper over `async_transform()` that allows `None` to be passed.\n\n    See `async_transform()` for more details.\n    \"\"\"\n    if data is None:\n        return None\n    return await async_transform(data, expected_type)\n\n\nasync def async_transform(\n        data: _T,\n        expected_type: object,\n) -> _T:\n    \"\"\"Transform dictionaries based off of type information from the given type, for example:\n\n    ```py\n    class Params(TypedDict, total=False):\n        card_id: Required[Annotated[str, PropertyInfo(alias=\"cardID\")]]\n\n\n    transformed = transform({\"card_id\": \"<my card ID>\"}, Params)\n    # {'cardID': '<my card ID>'}\n    ```\n\n    Any keys / data that does not have type information given will be included as is.\n\n    It should be noted that the transformations that this function does are not represented in the type system.\n    \"\"\"\n    transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type))\n    return cast(_T, transformed)\n\n\nasync def _async_transform_recursive(\n        data: object,\n        *,\n        annotation: type,\n        inner_type: type | None = None,\n) -> object:\n    \"\"\"Transform the given data against the expected type.\n\n    Args:\n        annotation: The direct type annotation given to the particular piece of data.\n            This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc\n\n        inner_type: If applicable, this is the \"inside\" type. This is useful in certain cases where the outside type\n            is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in\n            the list can be transformed using the metadata from the container type.\n\n            Defaults to the same value as the `annotation` argument.\n    \"\"\"\n    if inner_type is None:\n        inner_type = annotation\n\n    stripped_type = strip_annotated_type(inner_type)\n    if is_typeddict(stripped_type) and is_mapping(data):\n        return await _async_transform_typeddict(data, stripped_type)\n\n    if (\n            # List[T]\n            (is_list_type(stripped_type) and is_list(data))\n            # Iterable[T]\n            or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))\n    ):\n        inner_type = extract_type_arg(stripped_type, 0)\n        return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]\n\n    if is_union_type(stripped_type):\n        # For union types we run the transformation against all subtypes to ensure that everything is transformed.\n        #\n        # TODO: there may be edge cases where the same normalized field name will transform to two different names\n        # in different subtypes.\n        for subtype in get_args(stripped_type):\n            data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype)\n        return data\n\n    if isinstance(data, pydantic.BaseModel):\n        return model_dump(data, exclude_unset=True)\n\n    annotated_type = _get_annotated_type(annotation)\n    if annotated_type is None:\n        return data\n\n    # ignore the first argument as it is the actual type\n    annotations = get_args(annotated_type)[1:]\n    for annotation in annotations:\n        if isinstance(annotation, PropertyInfo) and annotation.format is not None:\n            return await _async_format_data(data, annotation.format, annotation.format_template)\n\n    return data\n\n\nasync def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:\n    if isinstance(data, (date, datetime)):\n        if format_ == \"iso8601\":\n            return data.isoformat()\n\n        if format_ == \"custom\" and format_template is not None:\n            return data.strftime(format_template)\n\n    if format_ == \"base64\" and is_base64_file_input(data):\n        binary: str | bytes | None = None\n\n        if isinstance(data, pathlib.Path):\n            binary = await anyio.Path(data).read_bytes()\n        elif isinstance(data, io.IOBase):\n            binary = data.read()\n\n            if isinstance(binary, str):  # type: ignore[unreachable]\n                binary = binary.encode()\n\n        if not isinstance(binary, bytes):\n            raise RuntimeError(f\"Could not read bytes from {data}; Received {type(binary)}\")\n\n        return base64.b64encode(binary).decode(\"ascii\")\n\n    return data\n\n\nasync def _async_transform_typeddict(\n        data: Mapping[str, object],\n        expected_type: type,\n) -> Mapping[str, object]:\n    result: dict[str, object] = {}\n    annotations = get_type_hints(expected_type, include_extras=True)\n    for key, value in data.items():\n        type_ = annotations.get(key)\n        if type_ is None:\n            # we do not have a type annotation for this field, leave it as is\n            result[key] = value\n        else:\n            result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)\n    return result\n"
  },
  {
    "path": "zhipuai/core/_utils/_typing.py",
    "content": "from __future__ import annotations\n\nfrom typing import Any, TypeVar, Iterable, cast\nfrom collections import abc as _c_abc\nfrom typing_extensions import Required, Annotated, get_args, get_origin\n\nfrom .._base_type import InheritsGeneric\nfrom zhipuai.core._base_compat import is_union as _is_union\n\n\ndef is_annotated_type(typ: type) -> bool:\n    return get_origin(typ) == Annotated\n\n\ndef is_list_type(typ: type) -> bool:\n    return (get_origin(typ) or typ) == list\n\n\ndef is_iterable_type(typ: type) -> bool:\n    \"\"\"If the given type is `typing.Iterable[T]`\"\"\"\n    origin = get_origin(typ) or typ\n    return origin == Iterable or origin == _c_abc.Iterable\n\n\ndef is_union_type(typ: type) -> bool:\n    return _is_union(get_origin(typ))\n\n\ndef is_required_type(typ: type) -> bool:\n    return get_origin(typ) == Required\n\n\ndef is_typevar(typ: type) -> bool:\n    # type ignore is required because type checkers\n    # think this expression will always return False\n    return type(typ) == TypeVar  # type: ignore\n\n\n# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]\ndef strip_annotated_type(typ: type) -> type:\n    if is_required_type(typ) or is_annotated_type(typ):\n        return strip_annotated_type(cast(type, get_args(typ)[0]))\n\n    return typ\n\n\ndef extract_type_arg(typ: type, index: int) -> type:\n    args = get_args(typ)\n    try:\n        return cast(type, args[index])\n    except IndexError as err:\n        raise RuntimeError(f\"Expected type {typ} to have a type argument at index {index} but it did not\") from err\n\n\ndef extract_type_var_from_base(\n    typ: type,\n    *,\n    generic_bases: tuple[type, ...],\n    index: int,\n    failure_message: str | None = None,\n) -> type:\n    \"\"\"Given a type like `Foo[T]`, returns the generic type variable `T`.\n\n    This also handles the case where a concrete subclass is given, e.g.\n    ```py\n    class MyResponse(Foo[bytes]):\n        ...\n\n    extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes\n    ```\n\n    And where a generic subclass is given:\n    ```py\n    _T = TypeVar('_T')\n    class MyResponse(Foo[_T]):\n        ...\n\n    extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes\n    ```\n    \"\"\"\n    cls = cast(object, get_origin(typ) or typ)\n    if cls in generic_bases:\n        # we're given the class directly\n        return extract_type_arg(typ, index)\n\n    # if a subclass is given\n    # ---\n    # this is needed as __orig_bases__ is not present in the typeshed stubs\n    # because it is intended to be for internal use only, however there does\n    # not seem to be a way to resolve generic TypeVars for inherited subclasses\n    # without using it.\n    if isinstance(cls, InheritsGeneric):\n        target_base_class: Any | None = None\n        for base in cls.__orig_bases__:\n            if base.__origin__ in generic_bases:\n                target_base_class = base\n                break\n\n        if target_base_class is None:\n            raise RuntimeError(\n                \"Could not find the generic base class;\\n\"\n                \"This should never happen;\\n\"\n                f\"Does {cls} inherit from one of {generic_bases} ?\"\n            )\n\n        extracted = extract_type_arg(target_base_class, index)\n        if is_typevar(extracted):\n            # If the extracted type argument is itself a type variable\n            # then that means the subclass itself is generic, so we have\n            # to resolve the type argument from the class itself, not\n            # the base class.\n            #\n            # Note: if there is more than 1 type argument, the subclass could\n            # change the ordering of the type arguments, this is not currently\n            # supported.\n            return extract_type_arg(typ, index)\n\n        return extracted\n\n    raise RuntimeError(failure_message or f\"Could not resolve inner type variable at index {index} for {typ}\")\n"
  },
  {
    "path": "zhipuai/core/_utils/_utils.py",
    "content": "from __future__ import annotations\n\nimport os\nimport re\nimport inspect\nimport functools\nfrom typing import (\n    Any,\n    Tuple,\n    Mapping,\n    TypeVar,\n    Callable,\n    Iterable,\n    Sequence,\n    cast,\n    overload, List, Union,\n)\nfrom pathlib import Path\nfrom typing_extensions import TypeGuard\n\nimport sniffio\n\nfrom .._base_type import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike\nfrom .._base_compat import parse_date as parse_date, parse_datetime as parse_datetime\n\n\ndef remove_notgiven_indict(obj):\n    if obj is None or (not isinstance(obj, Mapping)):\n        return obj\n    return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}\n\n\n_T = TypeVar(\"_T\")\n_TupleT = TypeVar(\"_TupleT\", bound=Tuple[object, ...])\n_MappingT = TypeVar(\"_MappingT\", bound=Mapping[str, object])\n_SequenceT = TypeVar(\"_SequenceT\", bound=Sequence[object])\nCallableT = TypeVar(\"CallableT\", bound=Callable[..., Any])\n\n\ndef flatten(t: Iterable[Iterable[_T]]) -> list[_T]:\n    return [item for sublist in t for item in sublist]\n\n\ndef extract_files(\n        # TODO: this needs to take Dict but variance issues.....\n        # create protocol type ?\n        query: Mapping[str, object],\n        *,\n        paths: Sequence[Sequence[str]],\n) -> list[tuple[str, FileTypes]]:\n    \"\"\"Recursively extract files from the given dictionary based on specified paths.\n\n    A path may look like this ['foo', 'files', '<array>', 'data'].\n\n    Note: this mutates the given dictionary.\n    \"\"\"\n    files: list[tuple[str, FileTypes]] = []\n    for path in paths:\n        files.extend(_extract_items(query, path, index=0, flattened_key=None))\n    return files\n\n\ndef _extract_items(\n        obj: object,\n        path: Sequence[str],\n        *,\n        index: int,\n        flattened_key: str | None,\n) -> list[tuple[str, FileTypes]]:\n    try:\n        key = path[index]\n    except IndexError:\n        if isinstance(obj, NotGiven):\n            # no value was provided - we can safely ignore\n            return []\n\n        # cyclical import\n        from .._files import assert_is_file_content\n\n        # We have exhausted the path, return the entry we found.\n        assert_is_file_content(obj, key=flattened_key)\n        assert flattened_key is not None\n        return [(flattened_key, cast(FileTypes, obj))]\n\n    index += 1\n    if is_dict(obj):\n        try:\n            # We are at the last entry in the path so we must remove the field\n            if (len(path)) == index:\n                item = obj.pop(key)\n            else:\n                item = obj[key]\n        except KeyError:\n            # Key was not present in the dictionary, this is not indicative of an error\n            # as the given path may not point to a required field. We also do not want\n            # to enforce required fields as the API may differ from the spec in some cases.\n            return []\n        if flattened_key is None:\n            flattened_key = key\n        else:\n            flattened_key += f\"[{key}]\"\n        return _extract_items(\n            item,\n            path,\n            index=index,\n            flattened_key=flattened_key,\n        )\n    elif is_list(obj):\n        if key != \"<array>\":\n            return []\n\n        return flatten(\n            [\n                _extract_items(\n                    item,\n                    path,\n                    index=index,\n                    flattened_key=flattened_key + \"[]\" if flattened_key is not None else \"[]\",\n                )\n                for item in obj\n            ]\n        )\n\n    # Something unexpected was passed, just ignore it.\n    return []\n\n\ndef is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]:\n    return not isinstance(obj, NotGiven)\n\n\n# Type safe methods for narrowing types with TypeVars.\n# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],\n# however this cause Pyright to rightfully report errors. As we know we don't\n# care about the contained types we can safely use `object` in it's place.\n#\n# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.\n# `is_*` is for when you're dealing with an unknown input\n# `is_*_t` is for when you're narrowing a known union type to a specific subset\n\n\ndef is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]:\n    return isinstance(obj, tuple)\n\n\ndef is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]:\n    return isinstance(obj, tuple)\n\n\ndef is_sequence(obj: object) -> TypeGuard[Sequence[object]]:\n    return isinstance(obj, Sequence)\n\n\ndef is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]:\n    return isinstance(obj, Sequence)\n\n\ndef is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]:\n    return isinstance(obj, Mapping)\n\n\ndef is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]:\n    return isinstance(obj, Mapping)\n\n\ndef is_dict(obj: object) -> TypeGuard[dict[object, object]]:\n    return isinstance(obj, dict)\n\n\ndef is_list(obj: object) -> TypeGuard[list[object]]:\n    return isinstance(obj, list)\n\n\ndef is_iterable(obj: object) -> TypeGuard[Iterable[object]]:\n    return isinstance(obj, Iterable)\n\n\ndef deepcopy_minimal(item: _T) -> _T:\n    \"\"\"Minimal reimplementation of copy.deepcopy() that will only copy certain object types:\n\n    - mappings, e.g. `dict`\n    - list\n\n    This is done for performance reasons.\n    \"\"\"\n    if is_mapping(item):\n        return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()})\n    if is_list(item):\n        return cast(_T, [deepcopy_minimal(entry) for entry in item])\n    return item\n\n\n# copied from https://github.com/Rapptz/RoboDanny\ndef human_join(seq: Sequence[str], *, delim: str = \", \", final: str = \"or\") -> str:\n    size = len(seq)\n    if size == 0:\n        return \"\"\n\n    if size == 1:\n        return seq[0]\n\n    if size == 2:\n        return f\"{seq[0]} {final} {seq[1]}\"\n\n    return delim.join(seq[:-1]) + f\" {final} {seq[-1]}\"\n\n\ndef quote(string: str) -> str:\n    \"\"\"Add single quotation marks around the given string. Does *not* do any escaping.\"\"\"\n    return f\"'{string}'\"\n\n\ndef required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]:\n    \"\"\"Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function.\n\n    Useful for enforcing runtime validation of overloaded functions.\n\n    Example usage:\n    ```py\n    @overload\n    def foo(*, a: str) -> str:\n        ...\n\n\n    @overload\n    def foo(*, b: bool) -> str:\n        ...\n\n\n    # This enforces the same constraints that a static type checker would\n    # i.e. that either a or b must be passed to the function\n    @required_args([\"a\"], [\"b\"])\n    def foo(*, a: str | None = None, b: bool | None = None) -> str:\n        ...\n    ```\n    \"\"\"\n\n    def inner(func: CallableT) -> CallableT:\n        params = inspect.signature(func).parameters\n        positional = [\n            name\n            for name, param in params.items()\n            if param.kind\n               in {\n                   param.POSITIONAL_ONLY,\n                   param.POSITIONAL_OR_KEYWORD,\n               }\n        ]\n\n        @functools.wraps(func)\n        def wrapper(*args: object, **kwargs: object) -> object:\n            given_params: set[str] = set()\n            for i, _ in enumerate(args):\n                try:\n                    given_params.add(positional[i])\n                except IndexError:\n                    raise TypeError(\n                        f\"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given\"\n                    ) from None\n\n            for key in kwargs.keys():\n                given_params.add(key)\n\n            for variant in variants:\n                matches = all((param in given_params for param in variant))\n                if matches:\n                    break\n            else:  # no break\n                if len(variants) > 1:\n                    variations = human_join(\n                        [\"(\" + human_join([quote(arg) for arg in variant], final=\"and\") + \")\" for variant in variants]\n                    )\n                    msg = f\"Missing required arguments; Expected either {variations} arguments to be given\"\n                else:\n                    # TODO: this error message is not deterministic\n                    missing = list(set(variants[0]) - given_params)\n                    if len(missing) > 1:\n                        msg = f\"Missing required arguments: {human_join([quote(arg) for arg in missing])}\"\n                    else:\n                        msg = f\"Missing required argument: {quote(missing[0])}\"\n                raise TypeError(msg)\n            return func(*args, **kwargs)\n\n        return wrapper  # type: ignore\n\n    return inner\n\n\n_K = TypeVar(\"_K\")\n_V = TypeVar(\"_V\")\n\n\n@overload\ndef strip_not_given(obj: None) -> None:\n    ...\n\n\n@overload\ndef strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]:\n    ...\n\n\n@overload\ndef strip_not_given(obj: object) -> object:\n    ...\n\n\ndef strip_not_given(obj: object | None) -> object:\n    \"\"\"Remove all top-level keys where their values are instances of `NotGiven`\"\"\"\n    if obj is None:\n        return None\n\n    if not is_mapping(obj):\n        return obj\n\n    return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}\n\n\ndef coerce_integer(val: str) -> int:\n    return int(val, base=10)\n\n\ndef coerce_float(val: str) -> float:\n    return float(val)\n\n\ndef coerce_boolean(val: str) -> bool:\n    return val == \"true\" or val == \"1\" or val == \"on\"\n\n\ndef maybe_coerce_integer(val: str | None) -> int | None:\n    if val is None:\n        return None\n    return coerce_integer(val)\n\n\ndef maybe_coerce_float(val: str | None) -> float | None:\n    if val is None:\n        return None\n    return coerce_float(val)\n\n\ndef maybe_coerce_boolean(val: str | None) -> bool | None:\n    if val is None:\n        return None\n    return coerce_boolean(val)\n\n\ndef removeprefix(string: str, prefix: str) -> str:\n    \"\"\"Remove a prefix from a string.\n\n    Backport of `str.removeprefix` for Python < 3.9\n    \"\"\"\n    if string.startswith(prefix):\n        return string[len(prefix):]\n    return string\n\n\ndef removesuffix(string: str, suffix: str) -> str:\n    \"\"\"Remove a suffix from a string.\n\n    Backport of `str.removesuffix` for Python < 3.9\n    \"\"\"\n    if string.endswith(suffix):\n        return string[: -len(suffix)]\n    return string\n\n\ndef file_from_path(path: str) -> FileTypes:\n    contents = Path(path).read_bytes()\n    file_name = os.path.basename(path)\n    return (file_name, contents)\n\n\ndef get_required_header(headers: HeadersLike, header: str) -> str:\n    lower_header = header.lower()\n    if isinstance(headers, Mapping):\n        headers = cast(Headers, headers)\n        for k, v in headers.items():\n            if k.lower() == lower_header and isinstance(v, str):\n                return v\n\n    \"\"\" to deal with the case where the header looks like Stainless-Event-Id \"\"\"\n    intercaps_header = re.sub(r\"([^\\w])(\\w)\", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize())\n\n    for normalized_header in [header, lower_header, header.upper(), intercaps_header]:\n        value = headers.get(normalized_header)\n        if value:\n            return value\n\n    raise ValueError(f\"Could not find {header} header\")\n\n\ndef get_async_library() -> str:\n    try:\n        return sniffio.current_async_library()\n    except Exception:\n        return \"false\"\n\n\ndef drop_prefix_image_data(content: Union[str, List[dict]]) -> Union[str, List[dict]]:\n    \"\"\"\n    删除 ;base64, 前缀\n    :param image_data:\n    :return:\n    \"\"\"\n    if isinstance(content, List):\n        for data in content:\n            if data.get('type') == 'image_url':\n                image_data = data.get(\"image_url\").get(\"url\")\n                if image_data.startswith(\"data:image/\"):\n                    image_data = image_data.split(\"base64,\")[-1]\n                    data[\"image_url\"][\"url\"] = image_data\n\n    return content\n"
  },
  {
    "path": "zhipuai/core/logs.py",
    "content": "import logging\nimport os\nimport time\n\nlogger = logging.getLogger(__name__)\n\n\nclass LoggerNameFilter(logging.Filter):\n    def filter(self, record):\n        # return record.name.startswith(\"loom_core\") or record.name in \"ERROR\" or (\n        #         record.name.startswith(\"uvicorn.error\")\n        #         and record.getMessage().startswith(\"Uvicorn running on\")\n        # )\n        return True\n\n\ndef get_log_file(log_path: str, sub_dir: str):\n    \"\"\"\n    sub_dir should contain a timestamp.\n    \"\"\"\n    log_dir = os.path.join(log_path, sub_dir)\n    # Here should be creating a new directory each time, so `exist_ok=False`\n    os.makedirs(log_dir, exist_ok=False)\n    return os.path.join(log_dir, \"zhipuai.log\")\n\n\ndef get_config_dict(\n    log_level: str, log_file_path: str, log_backup_count: int, log_max_bytes: int\n) -> dict:\n    # for windows, the path should be a raw string.\n    log_file_path = (\n        log_file_path.encode(\"unicode-escape\").decode()\n        if os.name == \"nt\"\n        else log_file_path\n    )\n    log_level = log_level.upper()\n    config_dict = {\n        \"version\": 1,\n        \"disable_existing_loggers\": False,\n        \"formatters\": {\n            \"formatter\": {\n                \"format\": (\n                    \"%(asctime)s %(name)-12s %(process)d %(levelname)-8s %(message)s\"\n                )\n            },\n        },\n        \"filters\": {\n            \"logger_name_filter\": {\n                \"()\": __name__ + \".LoggerNameFilter\",\n            },\n        },\n        \"handlers\": {\n            \"stream_handler\": {\n                \"class\": \"logging.StreamHandler\",\n                \"formatter\": \"formatter\",\n                \"level\": log_level,\n                # \"stream\": \"ext://sys.stdout\",\n                # \"filters\": [\"logger_name_filter\"],\n            },\n            \"file_handler\": {\n                \"class\": \"logging.handlers.RotatingFileHandler\",\n                \"formatter\": \"formatter\",\n                \"level\": log_level,\n                \"filename\": log_file_path,\n                \"mode\": \"a\",\n                \"maxBytes\": log_max_bytes,\n                \"backupCount\": log_backup_count,\n                \"encoding\": \"utf8\",\n            },\n        },\n        \"loggers\": {\n            \"loom_core\": {\n                \"handlers\": [\"stream_handler\", \"file_handler\"],\n                \"level\": log_level,\n                \"propagate\": False,\n            }\n        },\n        \"root\": {\n            \"level\": log_level,\n            \"handlers\": [\"stream_handler\", \"file_handler\"],\n        },\n    }\n    return config_dict\n\n\ndef get_timestamp_ms():\n    t = time.time()\n    return int(round(t * 1000))\n"
  },
  {
    "path": "zhipuai/core/pagination.py",
    "content": "# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.\n\nfrom typing import Any, List, Generic, TypeVar, Optional, cast\nfrom typing_extensions import Protocol, override, runtime_checkable\n\nfrom ._http_client import BasePage, PageInfo, BaseSyncPage\n\n__all__ = [\"SyncPage\", \"SyncCursorPage\"]\n\n_T = TypeVar(\"_T\")\n\n\n@runtime_checkable\nclass CursorPageItem(Protocol):\n    id: Optional[str]\n\n\nclass SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):\n    \"\"\"Note: no pagination actually occurs yet, this is for forwards-compatibility.\"\"\"\n\n    data: List[_T]\n    object: str\n\n    @override\n    def _get_page_items(self) -> List[_T]:\n        data = self.data\n        if not data:\n            return []\n        return data\n\n    @override\n    def next_page_info(self) -> None:\n        \"\"\"\n        This page represents a response that isn't actually paginated at the API level\n        so there will never be a next page.\n        \"\"\"\n        return None\n\n\nclass SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):\n    data: List[_T]\n\n    @override\n    def _get_page_items(self) -> List[_T]:\n        data = self.data\n        if not data:\n            return []\n        return data\n\n    @override\n    def next_page_info(self) -> Optional[PageInfo]:\n        data = self.data\n        if not data:\n            return None\n\n        item = cast(Any, data[-1])\n        if not isinstance(item, CursorPageItem) or item.id is None:\n            # TODO emit warning log\n            return None\n\n        return PageInfo(params={\"after\": item.id})\n"
  },
  {
    "path": "zhipuai/types/__init__.py",
    "content": ""
  },
  {
    "path": "zhipuai/types/agents/__init__.py",
    "content": ""
  },
  {
    "path": "zhipuai/types/agents/agents_completion.py",
    "content": "from typing import List, Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\"AgentsCompletion\", \"AgentsCompletionUsage\"]\n\nclass AgentsCompletionMessage(BaseModel):\n    content: Optional[object] = None\n    role: str\n\nclass AgentsCompletionUsage(BaseModel):\n    prompt_tokens: int\n    completion_tokens: int\n    total_tokens: int\n\n\nclass AgentsCompletionChoice(BaseModel):\n    index: int\n    finish_reason: str\n    message: AgentsCompletionMessage\n\nclass AgentsError(BaseModel):\n    code: Optional[str] = None\n    message: Optional[str] = None\n\n\nclass AgentsCompletion(BaseModel):\n    agent_id: Optional[str] = None\n    conversation_id: Optional[str] = None\n    status: Optional[str] = None\n    choices: List[AgentsCompletionChoice]\n    request_id: Optional[str] = None\n    id: Optional[str] = None\n    usage: Optional[AgentsCompletionUsage] = None\n    error: Optional[AgentsError] = None\n\n"
  },
  {
    "path": "zhipuai/types/agents/agents_completion_chunk.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"AgentsCompletionUsage\",\n    \"AgentsCompletionChunk\",\n    \"AgentsChoice\",\n    \"AgentsChoiceDelta\"\n]\n\n\nclass AgentsChoiceDelta(BaseModel):\n    content: Optional[object] = None\n    role: Optional[str] = None\n\n\nclass AgentsChoice(BaseModel):\n    delta: AgentsChoiceDelta\n    finish_reason: Optional[str] = None\n    index: int\n\n\nclass AgentsCompletionUsage(BaseModel):\n    prompt_tokens: int\n    completion_tokens: int\n    total_tokens: int\n\nclass AgentsError(BaseModel):\n    code: Optional[str] = None\n    message: Optional[str] = None\n\n\nclass AgentsCompletionChunk(BaseModel):\n    agent_id: Optional[str] = None\n    conversation_id: Optional[str] = None\n    id: Optional[str] = None\n    choices: List[AgentsChoice]\n    usage: Optional[AgentsCompletionUsage] = None\n    error: Optional[AgentsError] = None"
  },
  {
    "path": "zhipuai/types/agents/chat_completions_create_param.py",
    "content": "from typing import Optional\n\nfrom typing_extensions import TypedDict\n\n\nclass Reference(TypedDict, total=False):\n    enable: Optional[bool]\n    search_query: Optional[str]\n"
  },
  {
    "path": "zhipuai/types/assistant/__init__.py",
    "content": "\n\nfrom .assistant_completion import AssistantCompletion\n\n__all__ = [\n    'AssistantCompletion',\n]\n"
  },
  {
    "path": "zhipuai/types/assistant/assistant_completion.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom .message import MessageContent\nfrom ...core import BaseModel\n\n__all__ = [\"AssistantCompletion\", \"CompletionUsage\"]\n\n\nclass ErrorInfo(BaseModel):\n    code: str  # 错误码\n    message: str  # 错误信息\n\n\nclass AssistantChoice(BaseModel):\n    index: int  # 结果下标\n    delta: MessageContent  # 当前会话输出消息体\n    finish_reason: str\n    \"\"\"\n    # 推理结束原因 stop代表推理自然结束或触发停止词。  sensitive 代表模型推理内容被安全审核接口拦截。请注意，针对此类内容，请用户自行判断并决定是否撤回已公开的内容。 \n    # network_error 代表模型推理服务异常。\n    \"\"\"\n    metadata: dict  # 元信息，拓展字段\n\n\nclass CompletionUsage(BaseModel):\n    prompt_tokens: int  # 输入的 tokens 数量\n    completion_tokens: int  # 输出的 tokens 数量\n    total_tokens: int  # 总 tokens 数量\n\n\nclass AssistantCompletion(BaseModel):\n    id: str  # 请求 ID\n    conversation_id: str  # 会话 ID\n    assistant_id: str  # 智能体 ID\n    created: int  # 请求创建时间，Unix 时间戳\n    status: str  # 返回状态，包括：`completed` 表示生成结束`in_progress`表示生成中 `failed` 表示生成异常\n    last_error: Optional[ErrorInfo]  # 异常信息\n    choices: List[AssistantChoice]  # 增量返回的信息\n    metadata: Optional[Dict[str, Any]]  # 元信息，拓展字段\n    usage: Optional[CompletionUsage]  # tokens 数量统计\n"
  },
  {
    "path": "zhipuai/types/assistant/assistant_conversation_params.py",
    "content": "from typing import TypedDict, List, Optional, Union\n\n\nclass ConversationParameters(TypedDict, total=False):\n    assistant_id: str  # 智能体 ID\n    page: int  # 当前分页\n    page_size: int  # 分页数量\n"
  },
  {
    "path": "zhipuai/types/assistant/assistant_conversation_resp.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"ConversationUsageListResp\"\n]\n\nclass Usage(BaseModel):\n    prompt_tokens: int  # 用户输入的 tokens 数量\n    completion_tokens: int  # 模型输入的 tokens 数量\n    total_tokens: int  # 总 tokens 数量\n\n\nclass ConversationUsage(BaseModel):\n    id: str  # 会话 id\n    assistant_id: str  # 智能体Assistant id\n    create_time: int  # 创建时间\n    update_time: int  # 更新时间\n    usage: Usage  # 会话中 tokens 数量统计\n\n\nclass ConversationUsageList(BaseModel):\n    assistant_id: str  # 智能体id\n    has_more: bool  # 是否还有更多页\n    conversation_list: List[ConversationUsage]  # 返回的\n\n\nclass ConversationUsageListResp(BaseModel):\n    code: int\n    msg: str\n    data: ConversationUsageList\n"
  },
  {
    "path": "zhipuai/types/assistant/assistant_create_params.py",
    "content": "from typing import TypedDict, List, Optional, Union\n\n\nclass AssistantAttachments:\n    file_id: str\n\n\nclass MessageTextContent:\n    type: str  # 目前支持 type = text\n    text: str\n\n\nMessageContent = Union[MessageTextContent]\n\n\nclass ConversationMessage(TypedDict):\n    \"\"\"会话消息体\"\"\"\n    role: str  # 用户的输入角色，例如 'user'\n    content: List[MessageContent]  # 会话消息体的内容\n\n\nclass AssistantParameters(TypedDict, total=False):\n    \"\"\"智能体参数类\"\"\"\n    assistant_id: str  # 智能体 ID\n    conversation_id: Optional[str]  # 会话 ID，不传则创建新会话\n    model: str  # 模型名称，默认为 'GLM-4-Assistant'\n    stream: bool  # 是否支持流式 SSE，需要传入 True\n    messages: List[ConversationMessage]  # 会话消息体\n    attachments: Optional[List[AssistantAttachments]]  # 会话指定的文件，非必填\n    metadata: Optional[dict]  # 元信息，拓展字段，非必填\n\nclass TranslateParameters(TypedDict, total=False):\n    from_language: str\n    to_language: str\n\nclass ExtraParameters(TypedDict, total=False):\n    translate: TranslateParameters\n\n"
  },
  {
    "path": "zhipuai/types/assistant/assistant_support_resp.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom .message import MessageContent\nfrom ...core import BaseModel\n\n__all__ = [\n    \"AssistantSupportResp\"\n]\n\n\nclass AssistantSupport(BaseModel):\n    assistant_id: str  # 智能体的 Assistant id，用于智能体会话\n    created_at: int  # 创建时间\n    updated_at: int  # 更新时间\n    name: str  # 智能体名称\n    avatar: str  # 智能体头像\n    description: str  # 智能体描述\n    status: str  # 智能体状态，目前只有 publish\n    tools: List[str]  # 智能体支持的工具名\n    starter_prompts: List[str]  # 智能体启动推荐的 prompt\n\n\nclass AssistantSupportResp(BaseModel):\n    code: int\n    msg: str\n    data: List[AssistantSupport]  # 智能体列表\n"
  },
  {
    "path": "zhipuai/types/assistant/message/__init__.py",
    "content": "\nfrom .message_content import MessageContent\n\n__all__ = [\n    \"MessageContent\"\n]\n"
  },
  {
    "path": "zhipuai/types/assistant/message/message_content.py",
    "content": "\nfrom typing import Union\nfrom typing_extensions import Annotated, TypeAlias\n\nfrom ....core._utils import PropertyInfo\nfrom .tools_delta_block import ToolsDeltaBlock\nfrom .text_content_block import TextContentBlock\n\n__all__ = [\"MessageContent\"]\n\n\nMessageContent: TypeAlias = Annotated[\n    Union[ToolsDeltaBlock, TextContentBlock],\n    PropertyInfo(discriminator=\"type\"),\n]"
  },
  {
    "path": "zhipuai/types/assistant/message/text_content_block.py",
    "content": "from typing_extensions import Literal\n\nfrom ....core import BaseModel\n\n__all__ = [\"TextContentBlock\"]\n\n\nclass TextContentBlock(BaseModel):\n    content: str\n\n    role: str = \"assistant\"\n\n    type: Literal[\"content\"] = \"content\"\n    \"\"\"Always `content`.\"\"\"\n"
  },
  {
    "path": "zhipuai/types/assistant/message/tools/code_interpreter_delta_block.py",
    "content": "from typing import List\n\nfrom typing_extensions import Literal\n\n__all__ = [\"CodeInterpreterToolBlock\"]\n\nfrom .....core import BaseModel\n\n\nclass CodeInterpreterToolOutput(BaseModel):\n    \"\"\"代码工具输出结果\"\"\"\n    type: str  # 代码执行日志，目前只有 logs\n    logs: str  # 代码执行的日志结果\n    error_msg: str  # 错误信息\n\n\nclass CodeInterpreter(BaseModel):\n    \"\"\"代码解释器\"\"\"\n    input: str  # 生成的代码片段，输入给代码沙盒\n    outputs: List[CodeInterpreterToolOutput]  # 代码执行后的输出结果\n\n\nclass CodeInterpreterToolBlock(BaseModel):\n    \"\"\"代码工具块\"\"\"\n    code_interpreter: CodeInterpreter  # 代码解释器对象\n    type: Literal[\"code_interpreter\"]  # 调用工具的类型，始终为 `code_interpreter`\n"
  },
  {
    "path": "zhipuai/types/assistant/message/tools/drawing_tool_delta_block.py",
    "content": "from typing import List\n\nfrom typing_extensions import Literal\n\nfrom .....core import BaseModel\n\n__all__ = [\"DrawingToolBlock\"]\n\n\nclass DrawingToolOutput(BaseModel):\n    image: str\n\n\nclass DrawingTool(BaseModel):\n    input: str\n    outputs: List[DrawingToolOutput]\n\n\nclass DrawingToolBlock(BaseModel):\n    drawing_tool: DrawingTool\n\n    type: Literal[\"drawing_tool\"]\n    \"\"\"Always `drawing_tool`.\"\"\"\n"
  },
  {
    "path": "zhipuai/types/assistant/message/tools/function_delta_block.py",
    "content": "from typing import List, Union\n\nfrom typing_extensions import Literal\n\n__all__ = [\"FunctionToolBlock\"]\n\nfrom .....core import BaseModel\n\n\nclass FunctionToolOutput(BaseModel):\n    content: str\n\n\nclass FunctionTool(BaseModel):\n    name: str\n    arguments: Union[str,dict]\n    outputs: List[FunctionToolOutput]\n\n\nclass FunctionToolBlock(BaseModel):\n    function: FunctionTool\n\n    type: Literal[\"function\"]\n    \"\"\"Always `drawing_tool`.\"\"\""
  },
  {
    "path": "zhipuai/types/assistant/message/tools/retrieval_delta_black.py",
    "content": "from typing import List\n\nfrom typing_extensions import Literal\n\nfrom .....core import BaseModel\n\n\nclass RetrievalToolOutput(BaseModel):\n    \"\"\"\n    This class represents the output of a retrieval tool.\n\n    Attributes:\n    - text (str): The text snippet retrieved from the knowledge base.\n    - document (str): The name of the document from which the text snippet was retrieved, returned only in intelligent configuration.\n    \"\"\"\n    text: str\n    document: str\n\n\nclass RetrievalTool(BaseModel):\n    \"\"\"\n    This class represents the outputs of a retrieval tool.\n\n    Attributes:\n    - outputs (List[RetrievalToolOutput]): A list of text snippets and their respective document names retrieved from the knowledge base.\n    \"\"\"\n    outputs: List[RetrievalToolOutput]\n\n\nclass RetrievalToolBlock(BaseModel):\n    \"\"\"\n    This class represents a block for invoking the retrieval tool.\n\n    Attributes:\n    - retrieval (RetrievalTool): An instance of the RetrievalTool class containing the retrieval outputs.\n    - type (Literal[\"retrieval\"]): The type of tool being used, always set to \"retrieval\".\n    \"\"\"\n    retrieval: RetrievalTool\n    type: Literal[\"retrieval\"]\n    \"\"\"Always `retrieval`.\"\"\"\n\n"
  },
  {
    "path": "zhipuai/types/assistant/message/tools/tools_type.py",
    "content": "\nfrom typing import Union\nfrom typing_extensions import Annotated, TypeAlias\n\nfrom .code_interpreter_delta_block import CodeInterpreterToolBlock\nfrom .retrieval_delta_black import RetrievalToolBlock\nfrom .web_browser_delta_block import WebBrowserToolBlock\nfrom .....core._utils import PropertyInfo\nfrom .drawing_tool_delta_block import DrawingToolBlock\nfrom .function_delta_block import FunctionToolBlock\n\n__all__ = [\"ToolsType\"]\n\n\nToolsType: TypeAlias = Annotated[\n    Union[DrawingToolBlock, CodeInterpreterToolBlock, WebBrowserToolBlock, RetrievalToolBlock, FunctionToolBlock],\n    PropertyInfo(discriminator=\"type\"),\n]"
  },
  {
    "path": "zhipuai/types/assistant/message/tools/web_browser_delta_block.py",
    "content": "from typing import List\n\nfrom typing_extensions import Literal\n\nfrom .....core import BaseModel\n__all__ = [\"WebBrowserToolBlock\"]\n\n\nclass WebBrowserOutput(BaseModel):\n    \"\"\"\n    This class represents the output of a web browser search result.\n\n    Attributes:\n    - title (str): The title of the search result.\n    - link (str): The URL link to the search result's webpage.\n    - content (str): The textual content extracted from the search result.\n    - error_msg (str): Any error message encountered during the search or retrieval process.\n    \"\"\"\n    title: str\n    link: str\n    content: str\n    error_msg: str\n\n\nclass WebBrowser(BaseModel):\n    \"\"\"\n    This class represents the input and outputs of a web browser search.\n\n    Attributes:\n    - input (str): The input query for the web browser search.\n    - outputs (List[WebBrowserOutput]): A list of search results returned by the web browser.\n    \"\"\"\n    input: str\n    outputs: List[WebBrowserOutput]\n\n\nclass WebBrowserToolBlock(BaseModel):\n    \"\"\"\n    This class represents a block for invoking the web browser tool.\n\n    Attributes:\n    - web_browser (WebBrowser): An instance of the WebBrowser class containing the search input and outputs.\n    - type (Literal[\"web_browser\"]): The type of tool being used, always set to \"web_browser\".\n    \"\"\"\n    web_browser: WebBrowser\n    type: Literal[\"web_browser\"]\n"
  },
  {
    "path": "zhipuai/types/assistant/message/tools_delta_block.py",
    "content": "from typing import Optional, List\nfrom typing_extensions import Literal\n\nfrom .tools.tools_type import ToolsType\nfrom ....core import BaseModel\n\n__all__ = [\"ToolsDeltaBlock\"]\n\n\nclass ToolsDeltaBlock(BaseModel):\n    tool_calls: List[ToolsType]\n    \"\"\"The index of the content part in the message.\"\"\"\n\n    role: str = \"tool\"\n\n    type: Literal[\"tool_calls\"] = \"tool_calls\"\n    \"\"\"Always `tool_calls`.\"\"\"\n"
  },
  {
    "path": "zhipuai/types/audio/__init__.py",
    "content": "from .audio_speech_params import(\n    AudioSpeechParams\n)\n\nfrom .audio_customization_param import(\n    AudioCustomizationParam\n)\nfrom .transcriptions_create_param import(\n    TranscriptionsParam\n)\n\n__all__ = [\"AudioSpeechParams\",\"AudioCustomizationParam\",\"TranscriptionsParam\"]\n"
  },
  {
    "path": "zhipuai/types/audio/audio_customization_param.py",
    "content": "from __future__ import annotations\n\nfrom typing import List, Optional\n\nfrom typing_extensions import Literal, Required, TypedDict\n__all__ = [\"AudioCustomizationParam\"]\n\nfrom ..sensitive_word_check import SensitiveWordCheckRequest\n\nclass AudioCustomizationParam(TypedDict, total=False):\n    model: str\n    \"\"\"模型编码\"\"\"\n    input: str\n    \"\"\"需要生成语音的文本\"\"\"\n    voice_text: str\n    \"\"\"需要生成语音的音色\"\"\"\n    response_format: str\n    \"\"\"需要生成语音文件的格式\"\"\"\n    sensitive_word_check: Optional[SensitiveWordCheckRequest]\n    request_id: str\n    \"\"\"由用户端传参，需保证唯一性；用于区分每次请求的唯一标识，用户端不传时平台会默认生成。\"\"\"\n    user_id: str\n    \"\"\"用户端。\"\"\"\n\n\n"
  },
  {
    "path": "zhipuai/types/audio/audio_speech_chunk.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"AudioSpeechChunk\",\n    \"AudioError\",\n    \"AudioSpeechChoice\",\n    \"AudioSpeechDelta\"\n]\n\n\nclass AudioSpeechDelta(BaseModel):\n    content: Optional[str] = None\n    role: Optional[str] = None\n\n\nclass AudioSpeechChoice(BaseModel):\n    delta: AudioSpeechDelta\n    finish_reason: Optional[str] = None\n    index: int\n\nclass AudioError(BaseModel):\n    code: Optional[str] = None\n    message: Optional[str] = None\n\n\nclass AudioSpeechChunk(BaseModel):\n    choices: List[AudioSpeechChoice]\n    request_id: Optional[str] = None\n    created: Optional[int] = None\n    error: Optional[AudioError] = None"
  },
  {
    "path": "zhipuai/types/audio/audio_speech_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import List, Optional\n\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"AudioSpeechParams\"]\n\nfrom ..sensitive_word_check import SensitiveWordCheckRequest\n\n\nclass AudioSpeechParams(TypedDict, total=False):\n    model: str\n    \"\"\"模型编码\"\"\"\n    input: str\n    \"\"\"需要生成语音的文本\"\"\"\n    voice: str\n    \"\"\"需要生成语音的音色\"\"\"\n    response_format: str\n    \"\"\"需要生成语音文件的格式\"\"\"\n    sensitive_word_check: Optional[SensitiveWordCheckRequest]\n    request_id: str\n    \"\"\"由用户端传参，需保证唯一性；用于区分每次请求的唯一标识，用户端不传时平台会默认生成。\"\"\"\n    user_id: str\n    \"\"\"用户端。\"\"\"\n"
  },
  {
    "path": "zhipuai/types/audio/transcriptions_create_param.py",
    "content": "from __future__ import annotations\n\nfrom typing import List, Optional\n\nfrom typing_extensions import Literal, Required, TypedDict\n__all__ = [\"TranscriptionsParam\"]\n\nfrom ..sensitive_word_check import SensitiveWordCheckRequest\n\nclass TranscriptionsParam(TypedDict, total=False):\n    model: str\n    \"\"\"模型编码\"\"\"\n    temperature:float\n    \"\"\"采样温度\"\"\"\n    stream: bool\n    \"\"\"是否流式输出\"\"\"\n    sensitive_word_check: Optional[SensitiveWordCheckRequest]\n    request_id: str\n    \"\"\"由用户端传参，需保证唯一性；用于区分每次请求的唯一标识，用户端不传时平台会默认生成。\"\"\"\n    user_id: str\n    \"\"\"用户端。\"\"\""
  },
  {
    "path": "zhipuai/types/batch.py",
    "content": "# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.\n\nimport builtins\nfrom typing import List, Optional\nfrom typing_extensions import Literal\n\nfrom ..core import BaseModel\nfrom .batch_error import BatchError\nfrom .batch_request_counts import BatchRequestCounts\n\n__all__ = [\"Batch\", \"Errors\"]\n\n\nclass Errors(BaseModel):\n    data: Optional[List[BatchError]] = None\n\n    object: Optional[str] = None\n    \"\"\"这个类型，一直是`list`。\"\"\"\n\n\nclass Batch(BaseModel):\n    id: str\n\n    completion_window: str\n    \"\"\"用于执行请求的地址信息。\"\"\"\n\n    created_at: int\n    \"\"\"这是 Unix timestamp (in seconds) 表示的创建时间。\"\"\"\n\n    endpoint: str\n    \"\"\"这是ZhipuAI endpoint的地址。\"\"\"\n\n    input_file_id: str\n    \"\"\"标记为batch的输入文件的ID。\"\"\"\n\n    object: Literal[\"batch\"]\n    \"\"\"这个类型，一直是`batch`.\"\"\"\n\n    status: Literal[\n        \"validating\", \"failed\", \"in_progress\", \"finalizing\", \"completed\", \"expired\", \"cancelling\", \"cancelled\"\n    ]\n    \"\"\"batch 的状态。\"\"\"\n\n    cancelled_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的取消时间。\"\"\"\n\n    cancelling_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示发起取消的请求时间 \"\"\"\n\n    completed_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的完成时间。\"\"\"\n\n    error_file_id: Optional[str] = None\n    \"\"\"这个文件id包含了执行请求失败的请求的输出。\"\"\"\n\n    errors: Optional[Errors] = None\n\n    expired_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的将在过期时间。\"\"\"\n\n    expires_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 触发过期\"\"\"\n\n    failed_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的失败时间。\"\"\"\n\n    finalizing_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的最终时间。\"\"\"\n\n    in_progress_at: Optional[int] = None\n    \"\"\"Unix timestamp (in seconds) 表示的开始处理时间。\"\"\"\n\n    metadata: Optional[builtins.object] = None\n    \"\"\" \n    key:value形式的元数据，以便将信息存储\n        结构化格式。键的长度是64个字符，值最长512个字符\n    \"\"\"\n\n    output_file_id: Optional[str] = None\n    \"\"\"完成请求的输出文件的ID。\"\"\"\n\n    request_counts: Optional[BatchRequestCounts] = None\n    \"\"\"批次中不同状态的请求计数\"\"\"\n"
  },
  {
    "path": "zhipuai/types/batch_create_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"BatchCreateParams\"]\n\n\nclass BatchCreateParams(TypedDict, total=False):\n    completion_window: Required[str]\n    \"\"\"The time frame within which the batch should be processed.\n\n    Currently only `24h` is supported.\n    \"\"\"\n\n    endpoint: Required[Literal[\"/v1/chat/completions\", \"/v1/embeddings\"]]\n    \"\"\"The endpoint to be used for all requests in the batch.\n\n    Currently `/v1/chat/completions` and `/v1/embeddings` are supported.\n    \"\"\"\n\n    input_file_id: Required[str]\n    \"\"\"The ID of an uploaded file that contains requests for the new batch.\n\n    See [upload file](https://platform.openai.com/docs/api-reference/files/create)\n    for how to upload a file.\n\n    Your input file must be formatted as a\n    [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput),\n    and must be uploaded with the purpose `batch`.\n    \"\"\"\n\n    metadata: Optional[Dict[str, str]]\n    \"\"\"Optional custom metadata for the batch.\"\"\"\n\n    auto_delete_input_file: Optional[bool]\n"
  },
  {
    "path": "zhipuai/types/batch_error.py",
    "content": "# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.\n\nfrom typing import Optional\n\nfrom ..core import BaseModel\n\n__all__ = [\"BatchError\"]\n\n\nclass BatchError(BaseModel):\n    code: Optional[str] = None\n    \"\"\"定义的业务错误码\"\"\"\n\n    line: Optional[int] = None\n    \"\"\"文件中的行号\"\"\"\n\n    message: Optional[str] = None\n    \"\"\"关于对话文件中的错误的描述\"\"\"\n\n    param: Optional[str] = None\n    \"\"\"参数名称，如果有的话\"\"\"\n"
  },
  {
    "path": "zhipuai/types/batch_list_params.py",
    "content": "from __future__ import annotations\n\nfrom typing_extensions import TypedDict\n\n__all__ = [\"BatchListParams\"]\n\n\nclass BatchListParams(TypedDict, total=False):\n    after: str\n    \"\"\"分页的游标，用于获取下一页的数据。\n\n    `after` 是一个指向当前页面的游标，用于获取下一页的数据。如果没有提供 `after`，则返回第一页的数据。\n    list.\n    \"\"\"\n\n    limit: int\n    \"\"\"这个参数用于限制返回的结果数量。\n\n    Limit 用于限制返回的结果数量。默认值为 10\n    \"\"\"\n"
  },
  {
    "path": "zhipuai/types/batch_request_counts.py",
    "content": "from ..core import BaseModel\n\n__all__ = [\"BatchRequestCounts\"]\n\n\nclass BatchRequestCounts(BaseModel):\n    completed: int\n    \"\"\"这个数字表示已经完成的请求。\"\"\"\n\n    failed: int\n    \"\"\"这个数字表示失败的请求。\"\"\"\n\n    total: int\n    \"\"\"这个数字表示总的请求。\"\"\"\n"
  },
  {
    "path": "zhipuai/types/chat/__init__.py",
    "content": ""
  },
  {
    "path": "zhipuai/types/chat/async_chat_completion.py",
    "content": "from typing import List, Optional\n\nfrom ...core import BaseModel\nfrom .chat_completion import CompletionChoice, CompletionUsage\n\n__all__ = [\"AsyncTaskStatus\", \"AsyncCompletion\"]\n\n\nclass AsyncTaskStatus(BaseModel):\n    id: Optional[str] = None\n    request_id: Optional[str] = None\n    model: Optional[str] = None\n    task_status: Optional[str] = None\n\n\nclass AsyncCompletion(BaseModel):\n    id: Optional[str] = None\n    request_id: Optional[str] = None\n    model: Optional[str] = None\n    task_status: str\n    choices: List[CompletionChoice]\n    usage: CompletionUsage\n"
  },
  {
    "path": "zhipuai/types/chat/chat_completion.py",
    "content": "from typing import List, Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\"Completion\", \"CompletionUsage\"]\n\n\nclass Function(BaseModel):\n    arguments: str\n    name: str\n\n\nclass CompletionMessageToolCall(BaseModel):\n    id: str\n    function: Function\n    type: str\n\n\nclass CompletionMessage(BaseModel):\n    content: Optional[str] = None\n    role: str\n    reasoning_content: Optional[str] = None\n    tool_calls: Optional[List[CompletionMessageToolCall]] = None\n\n\nclass CompletionUsage(BaseModel):\n    prompt_tokens: int\n    completion_tokens: int\n    total_tokens: int\n\n\nclass CompletionChoice(BaseModel):\n    index: int\n    finish_reason: str\n    message: CompletionMessage\n\n\nclass Completion(BaseModel):\n    model: Optional[str] = None\n    created: Optional[int] = None\n    choices: List[CompletionChoice]\n    request_id: Optional[str] = None\n    id: Optional[str] = None\n    usage: CompletionUsage\n\n"
  },
  {
    "path": "zhipuai/types/chat/chat_completion_chunk.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"CompletionUsage\",\n    \"ChatCompletionChunk\",\n    \"Choice\",\n    \"ChoiceDelta\",\n    \"ChoiceDeltaFunctionCall\",\n    \"ChoiceDeltaToolCall\",\n    \"ChoiceDeltaToolCallFunction\",\n    \"AudioCompletionChunk\"\n]\n\n\nclass ChoiceDeltaFunctionCall(BaseModel):\n    arguments: Optional[str] = None\n    name: Optional[str] = None\n\n\nclass ChoiceDeltaToolCallFunction(BaseModel):\n    arguments: Optional[str] = None\n    name: Optional[str] = None\n\n\nclass ChoiceDeltaToolCall(BaseModel):\n    index: int\n    id: Optional[str] = None\n    function: Optional[ChoiceDeltaToolCallFunction] = None\n    type: Optional[str] = None\n\nclass AudioCompletionChunk(BaseModel):\n    id: Optional[str] = None\n    data: Optional[str] = None\n    expires_at: Optional[int] = None\n\n\nclass ChoiceDelta(BaseModel):\n    content: Optional[str] = None\n    role: Optional[str] = None\n    reasoning_content: Optional[str] = None\n    tool_calls: Optional[List[ChoiceDeltaToolCall]] = None\n    audio: Optional[AudioCompletionChunk] = None\n\n\nclass Choice(BaseModel):\n    delta: ChoiceDelta\n    finish_reason: Optional[str] = None\n    index: int\n\nclass PromptTokensDetails(BaseModel):\n    cached_tokens: int\n\nclass CompletionTokensDetails(BaseModel):\n    reasoning_tokens: int\n\nclass CompletionUsage(BaseModel):\n    prompt_tokens: int\n    prompt_tokens_details: Optional[PromptTokensDetails] = None\n    completion_tokens: int\n    completion_tokens_details: Optional[CompletionTokensDetails] = None\n    total_tokens: int\n\nclass ChatCompletionChunk(BaseModel):\n    id: Optional[str] = None\n    choices: List[Choice]\n    created: Optional[int] = None\n    model: Optional[str] = None\n    usage: Optional[CompletionUsage] = None\n    extra_json: Dict[str, Any]\n"
  },
  {
    "path": "zhipuai/types/chat/chat_completions_create_param.py",
    "content": "from typing import Optional\n\nfrom typing_extensions import TypedDict\n\n\nclass Reference(TypedDict, total=False):\n    enable: Optional[bool]\n    search_query: Optional[str]\n"
  },
  {
    "path": "zhipuai/types/chat/code_geex/code_geex_params.py",
    "content": "from typing import List, Optional\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\n    \"CodeGeexTarget\",\n    \"CodeGeexContext\",\n    \"CodeGeexExtra\",\n]\n\n\nclass CodeGeexTarget(TypedDict, total=False):\n    \"\"\"补全的内容参数\"\"\"\n    path: Optional[str]\n    \"\"\"文件路径\"\"\"\n    language: Required[Literal[\n        \"c\", \"c++\", \"cpp\", \"c#\", \"csharp\", \"c-sharp\", \"css\", \"cuda\", \"dart\", \"lua\",\n        \"objectivec\", \"objective-c\", \"objective-c++\", \"python\", \"perl\", \"prolog\",\n        \"swift\", \"lisp\", \"java\", \"scala\", \"tex\", \"jsx\", \"tsx\", \"vue\", \"markdown\",\n        \"html\", \"php\", \"js\", \"javascript\", \"typescript\", \"go\", \"shell\", \"rust\",\n        \"sql\", \"kotlin\", \"vb\", \"ruby\", \"pascal\", \"r\", \"fortran\", \"lean\", \"matlab\",\n        \"delphi\", \"scheme\", \"basic\", \"assembly\", \"groovy\", \"abap\", \"gdscript\",\n        \"haskell\", \"julia\", \"elixir\", \"excel\", \"clojure\", \"actionscript\", \"solidity\",\n        \"powershell\", \"erlang\", \"cobol\", \"alloy\", \"awk\", \"thrift\", \"sparql\", \"augeas\",\n        \"cmake\", \"f-sharp\", \"stan\", \"isabelle\", \"dockerfile\", \"rmarkdown\",\n        \"literate-agda\", \"tcl\", \"glsl\", \"antlr\", \"verilog\", \"racket\", \"standard-ml\",\n        \"elm\", \"yaml\", \"smalltalk\", \"ocaml\", \"idris\", \"visual-basic\", \"protocol-buffer\",\n        \"bluespec\", \"applescript\", \"makefile\", \"tcsh\", \"maple\", \"systemverilog\",\n        \"literate-coffeescript\", \"vhdl\", \"restructuredtext\", \"sas\", \"literate-haskell\",\n        \"java-server-pages\", \"coffeescript\", \"emacs-lisp\", \"mathematica\", \"xslt\",\n        \"zig\", \"common-lisp\", \"stata\", \"agda\", \"ada\"\n    ]]\n    \"\"\"代码语言类型，如python\"\"\"\n    code_prefix: Required[str]\n    \"\"\"补全位置的前文\"\"\"\n    code_suffix: Required[str]\n    \"\"\"补全位置的后文\"\"\"\n\n\nclass CodeGeexContext(TypedDict, total=False):\n    \"\"\"附加代码\"\"\"\n    path: Required[str]\n    \"\"\"附加代码文件的路径\"\"\"\n    code: Required[str]\n    \"\"\"附加的代码内容\"\"\"\n\n\nclass CodeGeexExtra(TypedDict, total=False):\n    target: Required[CodeGeexTarget]\n    \"\"\"补全的内容参数\"\"\"\n    contexts: Optional[List[CodeGeexContext]]\n    \"\"\"附加代码\"\"\"\n"
  },
  {
    "path": "zhipuai/types/embeddings.py",
    "content": "from __future__ import annotations\n\nfrom typing import Optional, List\n\nfrom ..core import BaseModel\nfrom .chat.chat_completion import CompletionUsage\n__all__ = [\"Embedding\", \"EmbeddingsResponded\"]\n\n\nclass Embedding(BaseModel):\n    object: str\n    index: Optional[int] = None\n    embedding: List[float]\n\n\nclass EmbeddingsResponded(BaseModel):\n    object: str\n    data: List[Embedding]\n    model: str\n    usage: CompletionUsage\n"
  },
  {
    "path": "zhipuai/types/file_parser/__init__.py",
    "content": ""
  },
  {
    "path": "zhipuai/types/file_parser/file_parser_create_params.py",
    "content": "from __future__ import annotations\n\nfrom typing_extensions import Literal, Required, TypedDict\nfrom ...core import NOT_GIVEN, Body, Headers, NotGiven, FileTypes\n\n\n__all__ = [\"FileParserCreateParams\", \"FileParserDownloadParams\"]\n\n\nclass FileParserCreateParams(TypedDict):\n    file: FileTypes\n    \"\"\"上传的文件\"\"\"\n    file_type: str\n    \"\"\"文件类型\"\"\"\n    tool_type: Literal[\"simple\", \"doc2x\", \"tencent\", \"zhipu-pro\"]\n    \"\"\"工具类型\"\"\"\n\n\nclass FileParserDownloadParams(TypedDict):\n    task_id: str\n    \"\"\"解析任务id\"\"\"\n    format_type: Literal[\"text\", \"download_link\"]\n    \"\"\"结果返回类型\"\"\"\n\n"
  },
  {
    "path": "zhipuai/types/file_parser/file_parser_resp.py",
    "content": "from typing import List, Optional\n\nfrom zhipuai.core import BaseModel\n\n__all__ = [\n    \"FileParserTaskCreateResp\"\n]\n\n\nclass FileParserTaskCreateResp(BaseModel):\n    task_id: str\n    # 任务id\n    message: str\n    # message\n    success: bool\n    # 是否成功\n\n\n"
  },
  {
    "path": "zhipuai/types/files/__init__.py",
    "content": "from .file_object import FileObject, ListOfFileObject\nfrom .upload_detail import UploadDetail\nfrom .file_deleted import FileDeleted\n\n__all__ = [\n    \"FileObject\",\n    \"ListOfFileObject\",\n    \"UploadDetail\",\n    \"FileDeleted\"\n]\n"
  },
  {
    "path": "zhipuai/types/files/file_create_params.py",
    "content": "\nfrom __future__ import annotations\n\nfrom typing import List, Optional\n\nfrom typing_extensions import Literal, Required, TypedDict\n\n\n__all__ = [\"FileCreateParams\"]\n\nfrom . import UploadDetail\nfrom ...core import FileTypes\n\n\nclass FileCreateParams(TypedDict, total=False):\n    file: FileTypes\n    \"\"\"file和 upload_detail二选一必填\"\"\"\n\n    upload_detail: List[UploadDetail]\n    \"\"\"file和 upload_detail二选一必填\"\"\"\n\n    purpose: Required[Literal[\"fine-tune\", \"retrieval\", \"batch\"]]\n    \"\"\" \n    上传文件的用途，支持 \"fine-tune和 \"retrieval\"\n    retrieval支持上传Doc、Docx、PDF、Xlsx、URL类型文件，且单个文件的大小不超过 5MB。\n    fine-tune支持上传.jsonl文件且当前单个文件的大小最大可为 100 MB ，文件中语料格式需满足微调指南中所描述的格式。\n    \"\"\"\n    custom_separator: Optional[List[str]]\n    \"\"\" \n    当 purpose 为 retrieval 且文件类型为 pdf, url, docx 时上传，切片规则默认为 `\\n`。\n    \"\"\"\n    knowledge_id: str\n    \"\"\" \n        当文件上传目的为  retrieval 时，需要指定知识库ID进行上传。\n    \"\"\"\n\n    sentence_size: int\n    \"\"\" \n        当文件上传目的为  retrieval 时，需要指定知识库ID进行上传。\n    \"\"\"\n"
  },
  {
    "path": "zhipuai/types/files/file_deleted.py",
    "content": "\nfrom typing_extensions import Literal\n\nfrom ...core import BaseModel\n__all__ = [\"FileDeleted\"]\n\n\nclass FileDeleted(BaseModel):\n    id: str\n\n    deleted: bool\n\n    object: Literal[\"file\"]\n"
  },
  {
    "path": "zhipuai/types/files/file_object.py",
    "content": "from typing import Optional, List\n\nfrom ...core import BaseModel\n\n__all__ = [\"FileObject\", \"ListOfFileObject\"]\n\n\nclass FileObject(BaseModel):\n\n    id: Optional[str] = None\n    bytes: Optional[int] = None\n    created_at: Optional[int] = None\n    filename: Optional[str] = None\n    object: Optional[str] = None\n    purpose: Optional[str] = None\n    status: Optional[str] = None\n    status_details: Optional[str] = None\n\n\nclass ListOfFileObject(BaseModel):\n\n    object: Optional[str] = None\n    data: List[FileObject]\n    has_more: Optional[bool] = None\n"
  },
  {
    "path": "zhipuai/types/files/upload_detail.py",
    "content": "from typing import Optional, List, Dict\n\nfrom ...core import BaseModel\n\n\nclass UploadDetail(BaseModel):\n    url: str\n    knowledge_type: int\n    file_name: Optional[str] = None\n    sentence_size: Optional[int] = None\n    custom_separator: Optional[List[str]] = None\n    callback_url: Optional[str] = None\n    callback_header: Optional[Dict[str,str]] = None\n"
  },
  {
    "path": "zhipuai/types/fine_tuning/__init__.py",
    "content": "from __future__ import annotations\n\nfrom .fine_tuning_job import FineTuningJob as FineTuningJob\nfrom .fine_tuning_job import ListOfFineTuningJob as ListOfFineTuningJob\nfrom .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent\n"
  },
  {
    "path": "zhipuai/types/fine_tuning/fine_tuning_job.py",
    "content": "from typing import List, Union, Optional, Dict, Any\n\nfrom ...core import BaseModel\n\n__all__ = [\"FineTuningJob\", \"Error\", \"Hyperparameters\", \"ListOfFineTuningJob\" ]\n\n\nclass Error(BaseModel):\n    code: str\n    message: str\n    param: Optional[str] = None\n\n\nclass Hyperparameters(BaseModel):\n    n_epochs: Union[str, int, None] = None\n\n\nclass FineTuningJob(BaseModel):\n    id: Optional[str] = None\n\n    request_id: Optional[str] = None\n\n    created_at: Optional[int] = None\n\n    error: Optional[Error] = None\n\n    fine_tuned_model: Optional[str] = None\n\n    finished_at: Optional[int] = None\n\n    hyperparameters: Optional[Hyperparameters] = None\n\n    model: Optional[str] = None\n\n    object: Optional[str] = None\n\n    result_files: List[str]\n\n    status: str\n\n    trained_tokens: Optional[int] = None\n\n    training_file: str\n\n    validation_file: Optional[str] = None\n\n\nclass ListOfFineTuningJob(BaseModel):\n    object: Optional[str] = None\n    data: List[FineTuningJob]\n    has_more: Optional[bool] = None\n"
  },
  {
    "path": "zhipuai/types/fine_tuning/fine_tuning_job_event.py",
    "content": "from typing import List, Union, Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\"FineTuningJobEvent\", \"Metric\", \"JobEvent\"]\n\n\nclass Metric(BaseModel):\n    epoch: Optional[Union[str, int, float]] = None\n    current_steps: Optional[int] = None\n    total_steps: Optional[int] = None\n    elapsed_time: Optional[str] = None\n    remaining_time: Optional[str] = None\n    trained_tokens: Optional[int] = None\n    loss: Optional[Union[str, int, float]] = None\n    eval_loss: Optional[Union[str, int, float]] = None\n    acc: Optional[Union[str, int, float]] = None\n    eval_acc: Optional[Union[str, int, float]] = None\n    learning_rate: Optional[Union[str, int, float]] = None\n\n\nclass JobEvent(BaseModel):\n    object: Optional[str] = None\n    id: Optional[str] = None\n    type: Optional[str] = None\n    created_at: Optional[int] = None\n    level: Optional[str] = None\n    message: Optional[str] = None\n    data: Optional[Metric] = None\n\n\nclass FineTuningJobEvent(BaseModel):\n    object: Optional[str] = None\n    data: List[JobEvent]\n    has_more: Optional[bool] = None\n"
  },
  {
    "path": "zhipuai/types/fine_tuning/job_create_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import Union\n\nfrom typing_extensions import Literal, TypedDict\n\n__all__ = [\"Hyperparameters\"]\n\n\nclass Hyperparameters(TypedDict, total=False):\n    batch_size: Union[Literal[\"auto\"], int]\n\n    learning_rate_multiplier: Union[Literal[\"auto\"], float]\n\n    n_epochs: Union[Literal[\"auto\"], int]\n"
  },
  {
    "path": "zhipuai/types/fine_tuning/models/__init__.py",
    "content": "\nfrom .fine_tuned_models import FineTunedModelsStatus"
  },
  {
    "path": "zhipuai/types/fine_tuning/models/fine_tuned_models.py",
    "content": "from typing import List, Union, Optional, ClassVar\n\nfrom ....core import BaseModel, PYDANTIC_V2, ConfigDict\n\n__all__ = [\"FineTunedModelsStatus\"]\n\n\nclass FineTunedModelsStatus(BaseModel):\n    if PYDANTIC_V2:\n        model_config: ClassVar[ConfigDict] = ConfigDict(extra=\"allow\", protected_namespaces=())\n    request_id: str  #请求id\n    model_name: str  #模型名称\n    delete_status: str  #删除状态 deleting（删除中）, deleted （已删除）\n"
  },
  {
    "path": "zhipuai/types/image.py",
    "content": "from __future__ import annotations\n\nfrom typing import Optional, List\n\nfrom ..core import BaseModel\n\n__all__ = [\"GeneratedImage\", \"ImagesResponded\"]\n\n\nclass GeneratedImage(BaseModel):\n    b64_json: Optional[str] = None\n    url: Optional[str] = None\n    revised_prompt: Optional[str] = None\n\n\nclass ImagesResponded(BaseModel):\n    created: int\n    data: List[GeneratedImage]\n"
  },
  {
    "path": "zhipuai/types/knowledge/__init__.py",
    "content": "\nfrom .knowledge import KnowledgeInfo\nfrom .knowledge_used import KnowledgeStatistics, KnowledgeUsed\n__all__ = [\n    'KnowledgeInfo',\n\n    \"KnowledgeStatistics\",\n    \"KnowledgeUsed\",\n]"
  },
  {
    "path": "zhipuai/types/knowledge/document/__init__.py",
    "content": "\nfrom .document import DocumentData, DocumentObject, DocumentSuccessinfo, DocumentFailedInfo\n\n\n__all__ = [\n    \"DocumentData\",\n    \"DocumentObject\",\n    \"DocumentSuccessinfo\",\n    \"DocumentFailedInfo\",\n]"
  },
  {
    "path": "zhipuai/types/knowledge/document/document.py",
    "content": "from typing import Optional, List\n\nfrom ....core import BaseModel\n\n__all__ = [\"DocumentData\", \"DocumentObject\", \"DocumentSuccessinfo\", \"DocumentFailedInfo\"]\n\n\nclass DocumentSuccessinfo(BaseModel):\n    documentId: Optional[str] = None\n    \"\"\"文件id\"\"\"\n    filename: Optional[str] = None\n    \"\"\"文件名称\"\"\"\n\n\nclass DocumentFailedInfo(BaseModel):\n    failReason: Optional[str] = None\n    \"\"\"上传失败的原因，包括：文件格式不支持、文件大小超出限制、知识库容量已满、容量上限为 50 万字。\"\"\"\n    filename: Optional[str] = None\n    \"\"\"文件名称\"\"\"\n    documentId: Optional[str] = None\n    \"\"\"知识库id\"\"\"\n\n\nclass DocumentObject(BaseModel):\n    \"\"\"文档信息\"\"\"\n\n    successInfos: Optional[List[DocumentSuccessinfo]] = None\n    \"\"\"上传成功的文件信息\"\"\"\n    failedInfos: Optional[List[DocumentFailedInfo]] = None\n    \"\"\"上传失败的文件信息\"\"\"\n\n\nclass DocumentDataFailInfo(BaseModel):\n    \"\"\"失败原因\"\"\"\n\n    embedding_code: Optional[int] = None  # 失败码 10001：知识不可用，知识库空间已达上限 10002：知识不可用，知识库空间已达上限(字数超出限制)\n    embedding_msg: Optional[str] = None  # 失败原因\n\n\nclass DocumentData(BaseModel):\n    id: str = None  # 知识唯一id\n    custom_separator: List[str] = None  # 切片规则\n    sentence_size: str = None  # 切片大小\n    length: int = None  # 文件大小（字节）\n    word_num: int = None  # 文件字数\n    name: str = None  # 文件名\n    url: str = None  # 文件下载链接\n    embedding_stat: int = None  # 0:向量化中 1:向量化完成 2:向量化失败\n    failInfo: Optional[DocumentDataFailInfo] = None  # 失败原因 向量化失败embedding_stat=2的时候 会有此值\n"
  },
  {
    "path": "zhipuai/types/knowledge/document/document_edit_params.py",
    "content": "from typing import Optional, List, Literal, TypedDict, Dict\n\n\n__all__ = [\"DocumentEditParams\"]\n\n\nclass DocumentEditParams(TypedDict):\n    \"\"\"\n    知识参数类型定义\n\n    Attributes:\n        id (str): 知识ID\n        knowledge_type (int): 知识类型:\n                        1:文章知识: 支持pdf,url,docx\n                        2.问答知识-文档:  支持pdf,url,docx\n                        3.问答知识-表格:  支持xlsx\n                        4.商品库-表格:  支持xlsx\n                        5.自定义:  支持pdf,url,docx\n        custom_separator (Optional[List[str]]): 当前知识类型为自定义(knowledge_type=5)时的切片规则，默认\\n\n        sentence_size (Optional[int]): 当前知识类型为自定义(knowledge_type=5)时的切片字数，取值范围: 20-2000，默认300\n        callback_url (Optional[str]): 回调地址\n        callback_header (Optional[dict]): 回调时携带的header\n    \"\"\"\n    id: str\n    knowledge_type: int\n    custom_separator: Optional[List[str]]\n    sentence_size: Optional[int]\n    callback_url: Optional[str]\n    callback_header: Optional[Dict[str, str]]"
  },
  {
    "path": "zhipuai/types/knowledge/document/document_list_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional\nfrom typing_extensions import Literal, Required, TypedDict\n\n\nclass DocumentListParams(TypedDict, total=False):\n    \"\"\"\n       文件查询参数类型定义\n\n       Attributes:\n           purpose (Optional[str]): 文件用途\n           knowledge_id (Optional[str]): 当文件用途为 retrieval 时，需要提供查询的知识库ID\n           page (Optional[int]): 页，默认1\n           limit (Optional[int]): 查询文件列表数，默认10\n           after (Optional[str]): 查询指定fileID之后的文件列表（当文件用途为 fine-tune 时需要）\n           order (Optional[str]): 排序规则，可选值['desc', 'asc']，默认desc（当文件用途为 fine-tune 时需要）\n       \"\"\"\n    purpose: Optional[str]\n    knowledge_id: Optional[str]\n    page: Optional[int]\n    limit: Optional[int]\n    after: Optional[str]\n    order: Optional[str]"
  },
  {
    "path": "zhipuai/types/knowledge/document/document_list_resp.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional, List\n\nfrom . import DocumentData\nfrom ....core import BaseModel\n\n__all__ = [\n    \"DocumentPage\"\n]\n\n\nclass DocumentPage(BaseModel):\n    list: List[DocumentData]\n    object: str\n"
  },
  {
    "path": "zhipuai/types/knowledge/knowledge.py",
    "content": "\nfrom typing import Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\"KnowledgeInfo\"]\n\n\nclass KnowledgeInfo(BaseModel):\n    id: Optional[str] = None\n    \"\"\"知识库唯一 id\"\"\"\n    embedding_id: Optional[str] = None    # 知识库绑定的向量化模型 见模型列表 [内部服务开放接口文档](https://lslfd0slxc.feishu.cn/docx/YauWdbBiMopV0FxB7KncPWCEn8f#H15NduiQZo3ugmxnWQFcfAHpnQ4)\n    name: Optional[str] = None    # 知识库名称 100字限制\n    customer_identifier: Optional[str] = None  # 用户标识 长度32位以内\n    description: Optional[str] = None  # 知识库描述 500字限制\n    background: Optional[str] = None  # 背景颜色（给枚举）'blue', 'red', 'orange', 'purple', 'sky'\n    icon: Optional[str] = None  # 知识库图标（给枚举） question: 问号、book: 书籍、seal: 印章、wrench: 扳手、tag: 标签、horn: 喇叭、house: 房子\n    bucket_id: Optional[str] = None  # 桶id 限制32位"
  },
  {
    "path": "zhipuai/types/knowledge/knowledge_create_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"KnowledgeBaseParams\"]\n\n\nclass KnowledgeBaseParams(TypedDict):\n    \"\"\"\n    知识库参数类型定义\n\n    Attributes:\n        embedding_id (int): 知识库绑定的向量化模型ID\n        name (str): 知识库名称，限制100字\n        customer_identifier (Optional[str]): 用户标识，长度32位以内\n        description (Optional[str]): 知识库描述，限制500字\n        background (Optional[Literal['blue', 'red', 'orange', 'purple', 'sky']]): 背景颜色\n        icon (Optional[Literal['question', 'book', 'seal', 'wrench', 'tag', 'horn', 'house']]): 知识库图标\n        bucket_id (Optional[str]): 桶ID，限制32位\n    \"\"\"\n    embedding_id: int\n    name: str\n    customer_identifier: Optional[str]\n    description: Optional[str]\n    background: Optional[Literal['blue', 'red', 'orange', 'purple', 'sky']] = None\n    icon: Optional[Literal['question', 'book', 'seal', 'wrench', 'tag', 'horn', 'house']] = None\n    bucket_id: Optional[str]"
  },
  {
    "path": "zhipuai/types/knowledge/knowledge_list_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"KnowledgeListParams\"]\n\n\nclass KnowledgeListParams(TypedDict, total=False):\n    page: int = 1\n    \"\"\" 页码，默认 1，第一页\n    \"\"\"\n\n    size: int = 10\n    \"\"\"每页数量 默认10\n    \"\"\"\n"
  },
  {
    "path": "zhipuai/types/knowledge/knowledge_list_resp.py",
    "content": "from __future__ import annotations\n\nfrom typing import Dict, Optional, List\n\nfrom . import KnowledgeInfo\nfrom ...core import BaseModel\n\n__all__ = [\n    \"KnowledgePage\"\n]\n\n\nclass KnowledgePage(BaseModel):\n    list: List[KnowledgeInfo]\n    object: str\n"
  },
  {
    "path": "zhipuai/types/knowledge/knowledge_used.py",
    "content": "\n\nfrom typing import Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"KnowledgeStatistics\",\n    \"KnowledgeUsed\"\n]\n\n\nclass KnowledgeStatistics(BaseModel):\n    \"\"\"\n    使用量统计\n    \"\"\"\n    word_num: Optional[int] = None\n    length: Optional[int] = None\n\n\nclass KnowledgeUsed(BaseModel):\n\n    used: Optional[KnowledgeStatistics] = None\n    \"\"\"已使用量\"\"\"\n    total: Optional[KnowledgeStatistics] = None\n    \"\"\"知识库总量\"\"\"\n"
  },
  {
    "path": "zhipuai/types/moderation/__init__.py",
    "content": ""
  },
  {
    "path": "zhipuai/types/moderation/moderation_completion.py",
    "content": "from typing import List, Optional, Union, Dict\n\nfrom ...core import BaseModel\n\n__all__ = [\"Completion\"]\n\nclass Completion(BaseModel):\n    model: Optional[str] = None\n    input: Optional[Union[str, List[str], Dict]] = None  # 新增 input 字段\n\n\n"
  },
  {
    "path": "zhipuai/types/sensitive_word_check/__init__.py",
    "content": "\n\nfrom .sensitive_word_check import SensitiveWordCheckRequest\n\n\n__all__ = [\n    \"SensitiveWordCheckRequest\"\n]\n"
  },
  {
    "path": "zhipuai/types/sensitive_word_check/sensitive_word_check.py",
    "content": "from typing import Optional\n\nfrom typing_extensions import TypedDict\n\n\nclass SensitiveWordCheckRequest(TypedDict, total=False):\n    type: Optional[str]\n    \"\"\"敏感词类型，当前仅支持ALL\"\"\"\n    status: Optional[str]\n    \"\"\"敏感词启用禁用状态\n        启用：ENABLE\n        禁用：DISABLE\n       备注：默认开启敏感词校验，如果要关闭敏感词校验，需联系商务获取对应权限，否则敏感词禁用不生效。\n    \"\"\""
  },
  {
    "path": "zhipuai/types/tools/__init__.py",
    "content": "from .web_search import (\n    WebSearch,\n    SearchIntent,\n    SearchResult,\n    SearchRecommend,\n)\n\nfrom .web_search_chunk import (\n    WebSearchChunk\n)\n\n__all__ = [\n    'WebSearch',\n    'SearchIntent',\n    'SearchResult',\n    'SearchRecommend',\n    'WebSearchChunk'\n]"
  },
  {
    "path": "zhipuai/types/tools/tools_web_search_params.py",
    "content": "\nfrom __future__ import annotations\n\nfrom typing import Union, Optional, List\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"WebSearchParams\"]\n\n\nclass WebSearchParams(TypedDict):\n    \"\"\"\n    工具名：web-search-pro参数类型定义\n\n    Attributes:\n        :param model: str, 模型名称\n        :param request_id: Optional[str], 请求ID\n        :param stream: Optional[bool], 是否流式\n        :param messages: Union[str, List[str], List[int], object, None],\n                        包含历史对话上下文的内容，按照 {\"role\": \"user\", \"content\": \"你好\"} 的json 数组形式进行传参\n                        当前版本仅支持 User Message 单轮对话，工具会理解User Message并进行搜索，\n                        请尽可能传入不带指令格式的用户原始提问，以提高搜索准确率。\n        :param scope: Optional[str], 指定搜索范围，全网、学术等，默认全网\n        :param location: Optional[str], 指定搜索用户地区 location 提高相关性\n        :param recent_days: Optional[int],支持指定返回 N 天（1-30）更新的搜索结果\n\n\n    \"\"\"\n    model: str\n    request_id: Optional[str]\n    stream: Optional[bool]\n    messages: Union[str, List[str], List[int], object, None]\n    scope: Optional[str] = None\n    location: Optional[str] = None\n    recent_days: Optional[int] = None"
  },
  {
    "path": "zhipuai/types/tools/web_search.py",
    "content": "from typing import List, Optional\n\nfrom ..chat.chat_completion import Function\nfrom ...core import BaseModel\n\n__all__ = [\n    \"WebSearch\",\n    \"SearchIntent\",\n    \"SearchResult\",\n    \"SearchRecommend\",\n]\n\n\nclass SearchIntent(BaseModel):\n    index: int\n    # 搜索轮次，默认为 0\n    query: str\n    # 搜索优化 query\n    intent: str\n    # 判断的意图类型\n    keywords: str\n    # 搜索关键词\n\n\nclass SearchResult(BaseModel):\n    index: int\n    # 搜索轮次，默认为 0\n    title: str\n    # 标题\n    link: str\n    # 链接\n    content: str\n    # 内容\n    icon: str\n    # 图标\n    media: str\n    # 来源媒体\n    refer: str\n    # 角标序号 [ref_1]\n\n\nclass SearchRecommend(BaseModel):\n    index: int\n    # 搜索轮次，默认为 0\n    query: str\n    # 推荐query\n\n\nclass WebSearchMessageToolCall(BaseModel):\n    id: str\n    search_intent: Optional[SearchIntent]\n    search_result: Optional[SearchResult]\n    search_recommend: Optional[SearchRecommend]\n    type: str\n\n\nclass WebSearchMessage(BaseModel):\n    role: str\n    tool_calls: Optional[List[WebSearchMessageToolCall]] = None\n\n\nclass WebSearchChoice(BaseModel):\n    index: int\n    finish_reason: str\n    message: WebSearchMessage\n\n\nclass WebSearch(BaseModel):\n    created: Optional[int] = None\n    choices: List[WebSearchChoice]\n    request_id: Optional[str] = None\n    id: Optional[str] = None\n"
  },
  {
    "path": "zhipuai/types/tools/web_search_chunk.py",
    "content": "from typing import List, Optional, Dict, Any\n\nfrom .web_search import SearchIntent, SearchResult, SearchRecommend\nfrom ...core import BaseModel\n\n__all__ = [\n    \"WebSearchChunk\"\n]\n\n\nclass ChoiceDeltaToolCall(BaseModel):\n    index: int\n    id: Optional[str] = None\n\n    search_intent: Optional[SearchIntent] = None\n    search_result: Optional[SearchResult] = None\n    search_recommend: Optional[SearchRecommend] = None\n    type: Optional[str] = None\n\n\nclass ChoiceDelta(BaseModel):\n    role: Optional[str] = None\n    tool_calls: Optional[List[ChoiceDeltaToolCall]] = None\n\n\nclass Choice(BaseModel):\n    delta: ChoiceDelta\n    finish_reason: Optional[str] = None\n    index: int\n\n\nclass WebSearchChunk(BaseModel):\n    id: Optional[str] = None\n    choices: List[Choice]\n    created: Optional[int] = None\n"
  },
  {
    "path": "zhipuai/types/video/__init__.py",
    "content": "\nfrom .video_object import (\n    VideoObject,\n    VideoResult\n)\n\n__all__ = [\"VideoObject\", \"VideoResult\"]\n"
  },
  {
    "path": "zhipuai/types/video/video_create_params.py",
    "content": "from __future__ import annotations\n\nfrom typing import List, Optional\n\nfrom typing_extensions import Literal, Required, TypedDict\n\n__all__ = [\"VideoCreateParams\"]\n\nfrom ..sensitive_word_check import SensitiveWordCheckRequest\n\n\nclass VideoCreateParams(TypedDict, total=False):\n    model: str\n    \"\"\"模型编码\"\"\"\n    prompt: str\n    \"\"\"所需视频的文本描述\"\"\"\n    image_url: str\n    \"\"\"所需视频的文本描述\"\"\"\n    sensitive_word_check: Optional[SensitiveWordCheckRequest]\n    \"\"\"支持 URL 或者 Base64、传入 image 奖进行图生视频\n     * 图片格式：\n     *   图片大小：\"\"\"\n    request_id: str\n    \"\"\"由用户端传参，需保证唯一性；用于区分每次请求的唯一标识，用户端不传时平台会默认生成。\"\"\"\n\n    user_id: str\n    \"\"\"用户端。\"\"\"\n"
  },
  {
    "path": "zhipuai/types/video/video_object.py",
    "content": "from typing import Optional, List\n\nfrom ...core import BaseModel\n\n__all__ = [\"VideoObject\", \"VideoResult\"]\n\n\nclass VideoResult(BaseModel):\n    url: str\n    \"\"\"视频url\"\"\"\n    cover_image_url: str\n    \"\"\"预览图\"\"\"\n\n\nclass VideoObject(BaseModel):\n    id: Optional[str] = None\n    \"\"\"智谱 AI 开放平台生成的任务订单号，调用请求结果接口时请使用此订单号\"\"\"\n\n    model: str\n    \"\"\"模型名称\"\"\"\n\n    video_result: List[VideoResult]\n    \"\"\"视频生成结果\"\"\"\n\n    task_status: str\n    \"\"\"处理状态，PROCESSING（处理中），SUCCESS（成功），FAIL（失败）\n    注：处理中状态需通过查询获取结果\"\"\"\n\n    request_id: str\n    \"\"\"用户在客户端请求时提交的任务编号或者平台生成的任务编号\"\"\"\n"
  },
  {
    "path": "zhipuai/types/web_search/__init__.py",
    "content": "\nfrom .web_search_create_params import (\n    WebSearchCreatParams\n)\n\n__all__ = [\"WebSearchCreatParams\"]\n"
  },
  {
    "path": "zhipuai/types/web_search/web_search_create_params.py",
    "content": "\nfrom __future__ import annotations\n\nfrom typing import Union, Optional, List\nfrom typing_extensions import Literal, Required, TypedDict\nfrom ...types.sensitive_word_check import SensitiveWordCheckRequest\n\n\n__all__ = [\"WebSearchCreatParams\"]\n\n\nclass WebSearchCreatParams(TypedDict):\n\n    search_engine: str\n    \"\"\"搜索引擎\"\"\"\n\n    search_query: str\n    \"\"\"搜索query文本\"\"\"\n\n    request_id: str\n    \"\"\"由用户端传参，需保证唯一性；用于区分每次请求的唯一标识，用户端不传时平台会默认生成。\"\"\"\n\n    user_id: str\n    \"\"\"用户端。\"\"\"\n\n    sensitive_word_check: Optional[SensitiveWordCheckRequest]\n"
  },
  {
    "path": "zhipuai/types/web_search/web_search_resp.py",
    "content": "from typing import List, Optional\n\nfrom ...core import BaseModel\n\n__all__ = [\n    \"SearchIntentResp\",\n    \"SearchResultResp\",\n]\n\n\nclass SearchIntentResp(BaseModel):\n    query: str\n    # 搜索优化 query\n    intent: str\n    # 判断的意图类型\n    keywords: str\n    # 搜索关键词\n\n\nclass SearchResultResp(BaseModel):\n    title: str\n    # 标题\n    link: str\n    # 链接\n    content: str\n    # 内容\n    icon: str\n    # 图标\n    media: str\n    # 来源媒体\n    refer: str\n    # 角标序号 [ref_1]\n    publish_date:  str\n    # 发布时间\n\nclass WebSearchResp(BaseModel):\n    created: Optional[int] = None\n    request_id: Optional[str] = None\n    id: Optional[str] = None\n    search_intent: Optional[SearchIntentResp]\n    search_result: Optional[SearchResultResp]\n\n"
  }
]