├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── live-api.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── rhub.yaml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── as-json.R ├── batch-chat.R ├── chat-parallel.R ├── chat-structured.R ├── chat.R ├── content-image.R ├── content-pdf.R ├── content-tools.R ├── content.R ├── deprecated.R ├── ellmer-package.R ├── httr2.R ├── import-standalone-obj-type.R ├── import-standalone-purrr.R ├── import-standalone-types-check.R ├── interpolate.R ├── params.R ├── provider-azure.R ├── provider-bedrock.R ├── provider-claude.R ├── provider-cloudflare.R ├── provider-cortex.R ├── provider-databricks.R ├── provider-deepseek.R ├── provider-gemini-upload.R ├── provider-gemini.R ├── provider-github.R ├── provider-groq.R ├── provider-huggingface.r ├── provider-mistral.R ├── provider-ollama.R ├── provider-openai.R ├── provider-openrouter.R ├── provider-perplexity.R ├── provider-portkey.R ├── provider-snowflake.R ├── provider-vllm.R ├── provider.R ├── shiny.R ├── sysdata.rda ├── tokens.R ├── tools-def-auto.R ├── tools-def.R ├── turns.R ├── types.R ├── utils-S7.R ├── utils-callbacks.R ├── utils-cat.R ├── utils-coro.R ├── utils-merge.R ├── utils-prettytime.R ├── utils.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── air.toml ├── codecov.yml ├── cran-comments.md ├── data-raw ├── openai.csv └── prices.R ├── ellmer.Rproj ├── inst ├── examples │ └── third-party-testing.txt ├── httr2.png └── tool_prompt.md ├── man ├── Chat.Rd ├── Content.Rd ├── Provider.Rd ├── Turn.Rd ├── Type.Rd ├── batch_chat.Rd ├── chat_anthropic.Rd ├── chat_aws_bedrock.Rd ├── chat_azure_openai.Rd ├── chat_cloudflare.Rd ├── chat_cortex_analyst.Rd ├── chat_databricks.Rd ├── chat_deepseek.Rd ├── chat_github.Rd ├── chat_google_gemini.Rd ├── chat_groq.Rd ├── chat_huggingface.Rd ├── chat_mistral.Rd ├── chat_ollama.Rd ├── chat_openai.Rd ├── chat_openrouter.Rd ├── chat_perplexity.Rd ├── chat_portkey.Rd ├── chat_snowflake.Rd ├── chat_vllm.Rd ├── content_image_url.Rd ├── content_pdf_file.Rd ├── contents_text.Rd ├── create_tool_def.Rd ├── deprecated.Rd ├── ellmer-package.Rd ├── figures │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── logo.png ├── google_upload.Rd ├── has_credentials.Rd ├── interpolate.Rd ├── live_console.Rd ├── parallel_chat.Rd ├── params.Rd ├── token_usage.Rd ├── tool.Rd ├── tool_annotations.Rd ├── tool_reject.Rd └── type_boolean.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── favicon.svg │ ├── site.webmanifest │ ├── web-app-manifest-192x192.png │ └── web-app-manifest-512x512.png ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── failures.md └── problems.md ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── batch-chat.md │ ├── chat-structured.md │ ├── chat.md │ ├── content-image.md │ ├── content-tools.md │ ├── content.md │ ├── interpolate.md │ ├── params.md │ ├── provider-azure.md │ ├── provider-bedrock.md │ ├── provider-claude.md │ ├── provider-cloudflare.md │ ├── provider-cortex.md │ ├── provider-databricks.md │ ├── provider-deepseek.md │ ├── provider-gemini.md │ ├── provider-groq.md │ ├── provider-huggingface.md │ ├── provider-mistral.md │ ├── provider-ollama.md │ ├── provider-openai.md │ ├── provider-openrouter.md │ ├── provider-portkey.md │ ├── provider-snowflake.md │ ├── tokens.md │ ├── tools-def-auto.md │ ├── tools-def.md │ ├── turns.md │ ├── utils-S7.md │ ├── utils-callbacks.md │ ├── utils-cat.md │ └── utils.md │ ├── apples.pdf │ ├── batch │ ├── state-capitals.json │ └── state-name.json │ ├── helper-async.R │ ├── helper-chat.R │ ├── helper-content-tools.R │ ├── helper-provider.R │ ├── teardown-usage.R │ ├── test-as-json.R │ ├── test-batch-chat.R │ ├── test-chat-parallel.R │ ├── test-chat-structured.R │ ├── test-chat.R │ ├── test-content-image.R │ ├── test-content-pdf.R │ ├── test-content-tools.R │ ├── test-content.R │ ├── test-deprecated.R │ ├── test-interpolate.R │ ├── test-params.R │ ├── test-provider-azure.R │ ├── test-provider-bedrock.R │ ├── test-provider-claude.R │ ├── test-provider-cloudflare.R │ ├── test-provider-cortex.R │ ├── test-provider-databricks.R │ ├── test-provider-deepseek.R │ ├── test-provider-gemini-upload.R │ ├── test-provider-gemini.R │ ├── test-provider-github.R │ ├── test-provider-groq.R │ ├── test-provider-huggingface.R │ ├── test-provider-mistral.R │ ├── test-provider-ollama.R │ ├── test-provider-openai.R │ ├── test-provider-openrouter.R │ ├── test-provider-portkey.R │ ├── test-provider-snowflake.R │ ├── test-provider-vllm.R │ ├── test-tokens.R │ ├── test-tools-def-auto.R │ ├── test-tools-def.R │ ├── test-turns.R │ ├── test-utils-S7.R │ ├── test-utils-callbacks.R │ ├── test-utils-cat.R │ ├── test-utils-merge.R │ ├── test-utils.R │ └── tools-def.R └── vignettes ├── .gitignore ├── congressional-assets.png ├── ellmer.Rmd ├── programming.Rmd ├── prompt-design.Rmd ├── prompt-design_cache └── html │ ├── __packages │ ├── code-basic_23c2af49b209e7f837cf1d5796fe658a.RData │ ├── code-basic_23c2af49b209e7f837cf1d5796fe658a.rdb │ ├── code-basic_23c2af49b209e7f837cf1d5796fe658a.rdx │ ├── code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.RData │ ├── code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdb │ ├── code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdx │ ├── code-new-feature_50952fd1cf498c080ef900a6b6566fea.RData │ ├── code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdb │ ├── code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdx │ ├── code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.RData │ ├── code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdb │ ├── code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdx │ ├── code-r_a315a31de513b9570329f8d6ba458bb4.RData │ ├── code-r_a315a31de513b9570329f8d6ba458bb4.rdb │ ├── code-r_a315a31de513b9570329f8d6ba458bb4.rdx │ ├── code-styles_e2dd9503e443bb18fb347d207d80d38d.RData │ ├── code-styles_e2dd9503e443bb18fb347d207d80d38d.rdb │ ├── code-styles_e2dd9503e443bb18fb347d207d80d38d.rdx │ ├── code-teacher_543ffae480ea561caf9310e4557e122f.RData │ ├── code-teacher_543ffae480ea561caf9310e4557e122f.rdb │ ├── code-teacher_543ffae480ea561caf9310e4557e122f.rdx │ ├── data-examples_4417be86e888119b66334c9d09d2e349.RData │ ├── data-examples_4417be86e888119b66334c9d09d2e349.rdb │ ├── data-examples_4417be86e888119b66334c9d09d2e349.rdx │ ├── data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.RData │ ├── data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdb │ ├── data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdx │ ├── data-structured_9d96a9eafa0565bc04fc1679b0291b91.RData │ ├── data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdb │ ├── data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdx │ ├── data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.RData │ ├── data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdb │ ├── data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdx │ ├── unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.RData │ ├── unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdb │ ├── unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdx │ ├── usage_7450dd5600850638f2f463ce382f71a9.RData │ ├── usage_7450dd5600850638f2f463ce382f71a9.rdb │ └── usage_7450dd5600850638f2f463ce382f71a9.rdx ├── streaming-async.Rmd ├── structured-data.Rmd ├── structured-data_cache └── html │ ├── __packages │ ├── basics-image_5394a0a58034be7a3ff41e5a7a40e01b.RData │ ├── basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdb │ ├── basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdx │ ├── basics-text_f9f3c2d2259df508e0ada9e224adbe7f.RData │ ├── basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdb │ ├── basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdx │ ├── examples-classification_345ace7803d5add79763d1565d129266.RData │ ├── examples-classification_345ace7803d5add79763d1565d129266.rdb │ ├── examples-classification_345ace7803d5add79763d1565d129266.rdx │ ├── examples-image_ba40428e2ad729994ce734eb071a80fe.RData │ ├── examples-image_ba40428e2ad729994ce734eb071a80fe.rdb │ ├── examples-image_ba40428e2ad729994ce734eb071a80fe.rdx │ ├── examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.RData │ ├── examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdb │ ├── examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdx │ ├── examples-sentiment_ef29c06557ae94b99008e5978fe525dd.RData │ ├── examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdb │ ├── examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdx │ ├── examples-summarisation_b4eeeab772279f10541991a197bc8c24.RData │ ├── examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdb │ ├── examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdx │ ├── examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.RData │ ├── examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdb │ ├── examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdx │ ├── setup_708b46f5a397cb58f728f88aa9935154.RData │ ├── setup_708b46f5a397cb58f728f88aa9935154.rdb │ ├── setup_708b46f5a397cb58f728f88aa9935154.rdx │ ├── type-optional_1ddc90c528cd0b05153da187c33083f0.RData │ ├── type-optional_1ddc90c528cd0b05153da187c33083f0.rdb │ ├── type-optional_1ddc90c528cd0b05153da187c33083f0.rdx │ ├── type-required_2b6b688f55ee2d8da6a62dfac030c328.RData │ ├── type-required_2b6b688f55ee2d8da6a62dfac030c328.rdb │ ├── type-required_2b6b688f55ee2d8da6a62dfac030c328.rdx │ ├── unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.RData │ ├── unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdb │ ├── unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdx │ ├── usage_a390555c32af75ba9192f9addd5b44d1.RData │ ├── usage_a390555c32af75ba9192f9addd5b44d1.rdb │ └── usage_a390555c32af75ba9192f9addd5b44d1.rdx ├── tool-calling-right.svg ├── tool-calling-wrong.svg └── tool-calling.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^ellmer\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^\.github$ 5 | ^codecov\.yml$ 6 | ^README\.Rmd$ 7 | ^_pkgdown\.yml$ 8 | ^docs$ 9 | ^pkgdown$ 10 | ^vignettes/articles$ 11 | _cache/ 12 | ^cran-comments\.md$ 13 | ^CRAN-SUBMISSION$ 14 | ^[\.]?air\.toml$ 15 | ^\.vscode$ 16 | ^data-raw$ 17 | ^revdep$ 18 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check.yaml 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - { os: macos-latest, r: "release" } 28 | 29 | - { os: windows-latest, r: "release" } 30 | 31 | - { os: ubuntu-latest, r: "devel", http-user-agent: "release" } 32 | - { os: ubuntu-latest, r: "release" } 33 | - { os: ubuntu-latest, r: "oldrel-1" } 34 | - { os: ubuntu-latest, r: "oldrel-2" } 35 | - { os: ubuntu-latest, r: "oldrel-3" } 36 | - { os: ubuntu-latest, r: 'oldrel-4' } 37 | 38 | env: 39 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 40 | R_KEEP_PKG_SOURCE: yes 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - uses: r-lib/actions/setup-pandoc@v2 46 | 47 | - uses: r-lib/actions/setup-r@v2 48 | with: 49 | r-version: ${{ matrix.config.r }} 50 | http-user-agent: ${{ matrix.config.http-user-agent }} 51 | use-public-rspm: true 52 | 53 | - uses: r-lib/actions/setup-r-dependencies@v2 54 | with: 55 | extra-packages: any::rcmdcheck 56 | needs: check 57 | 58 | - uses: r-lib/actions/check-r-package@v2 59 | with: 60 | upload-snapshots: true 61 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 62 | -------------------------------------------------------------------------------- /.github/workflows/live-api.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: main 10 | pull_request: 11 | branches: main 12 | 13 | name: live-api.yaml 14 | 15 | jobs: 16 | live-api: 17 | runs-on: ubuntu-latest 18 | 19 | env: 20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 21 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 22 | GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} 23 | ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} 24 | AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} 25 | R_KEEP_PKG_SOURCE: yes 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: r-lib/actions/setup-pandoc@v2 31 | 32 | - uses: r-lib/actions/setup-r@v2 33 | with: 34 | r-version: release 35 | http-user-agent: ${{ matrix.config.http-user-agent }} 36 | use-public-rspm: true 37 | 38 | - uses: r-lib/actions/setup-r-dependencies@v2 39 | with: 40 | extra-packages: any::rcmdcheck 41 | needs: check 42 | 43 | - uses: r-lib/actions/check-r-package@v2 44 | with: 45 | upload-snapshots: true 46 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 25 | ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} 26 | permissions: 27 | contents: write 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - uses: r-lib/actions/setup-pandoc@v2 32 | 33 | - uses: r-lib/actions/setup-r@v2 34 | with: 35 | use-public-rspm: true 36 | 37 | - uses: r-lib/actions/setup-r-dependencies@v2 38 | with: 39 | extra-packages: any::pkgdown, local::. 40 | needs: website 41 | 42 | - name: Build site 43 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 44 | shell: Rscript {0} 45 | 46 | - name: Deploy to GitHub pages 🚀 47 | if: github.event_name != 'pull_request' 48 | uses: JamesIves/github-pages-deploy-action@v4.5.0 49 | with: 50 | clean: false 51 | branch: gh-pages 52 | folder: docs 53 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: pr-commands.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: r-lib/actions/pr-fetch@v2 24 | with: 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::roxygen2 34 | needs: pr-document 35 | 36 | - name: Document 37 | run: roxygen2::roxygenise() 38 | shell: Rscript {0} 39 | 40 | - name: commit 41 | run: | 42 | git config --local user.name "$GITHUB_ACTOR" 43 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 44 | git add man/\* NAMESPACE 45 | git commit -m 'Document' 46 | 47 | - uses: r-lib/actions/pr-push@v2 48 | with: 49 | repo-token: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | style: 52 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 53 | name: style 54 | runs-on: ubuntu-latest 55 | env: 56 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 57 | permissions: 58 | contents: write 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - uses: r-lib/actions/pr-fetch@v2 63 | with: 64 | repo-token: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | - uses: r-lib/actions/setup-r@v2 67 | 68 | - name: Install dependencies 69 | run: install.packages("styler") 70 | shell: Rscript {0} 71 | 72 | - name: Style 73 | run: styler::style_pkg() 74 | shell: Rscript {0} 75 | 76 | - name: commit 77 | run: | 78 | git config --local user.name "$GITHUB_ACTOR" 79 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 80 | git add \*.R 81 | git commit -m 'Style' 82 | 83 | - uses: r-lib/actions/pr-push@v2 84 | with: 85 | repo-token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | .quarto 7 | docs 8 | inst/doc 9 | 10 | /.quarto/ 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Posit.air-vscode" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[r]": { 3 | "editor.formatOnSave": true, 4 | "editor.defaultFormatter": "Posit.air-vscode" 5 | }, 6 | "cSpell.words": [ 7 | "ellmer" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2024 2 | COPYRIGHT HOLDER: ellmer authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2024 ellmer authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method("[",ellmer_prompt) 4 | S3method(format,ellmer_dollars) 5 | S3method(print,Chat) 6 | S3method(print,ellmer_dollars) 7 | S3method(print,ellmer_prompt) 8 | export(Content) 9 | export(ContentImage) 10 | export(ContentImageInline) 11 | export(ContentImageRemote) 12 | export(ContentPDF) 13 | export(ContentText) 14 | export(ContentThinking) 15 | export(ContentToolRequest) 16 | export(ContentToolResult) 17 | export(Provider) 18 | export(Turn) 19 | export(TypeArray) 20 | export(TypeBasic) 21 | export(TypeEnum) 22 | export(TypeJsonSchema) 23 | export(TypeObject) 24 | export(batch_chat) 25 | export(batch_chat_completed) 26 | export(batch_chat_structured) 27 | export(chat_anthropic) 28 | export(chat_aws_bedrock) 29 | export(chat_azure) 30 | export(chat_azure_openai) 31 | export(chat_bedrock) 32 | export(chat_claude) 33 | export(chat_cloudflare) 34 | export(chat_cortex) 35 | export(chat_cortex_analyst) 36 | export(chat_databricks) 37 | export(chat_deepseek) 38 | export(chat_gemini) 39 | export(chat_github) 40 | export(chat_google_gemini) 41 | export(chat_google_vertex) 42 | export(chat_groq) 43 | export(chat_huggingface) 44 | export(chat_mistral) 45 | export(chat_ollama) 46 | export(chat_openai) 47 | export(chat_openrouter) 48 | export(chat_perplexity) 49 | export(chat_portkey) 50 | export(chat_snowflake) 51 | export(chat_vllm) 52 | export(content_image_file) 53 | export(content_image_plot) 54 | export(content_image_url) 55 | export(content_pdf_file) 56 | export(content_pdf_url) 57 | export(contents_html) 58 | export(contents_markdown) 59 | export(contents_text) 60 | export(create_tool_def) 61 | export(google_upload) 62 | export(has_credentials) 63 | export(interpolate) 64 | export(interpolate_file) 65 | export(interpolate_package) 66 | export(live_browser) 67 | export(live_console) 68 | export(models_anthropic) 69 | export(models_aws_bedrock) 70 | export(models_github) 71 | export(models_google_gemini) 72 | export(models_google_vertex) 73 | export(models_ollama) 74 | export(models_openai) 75 | export(models_portkey) 76 | export(models_vllm) 77 | export(parallel_chat) 78 | export(parallel_chat_structured) 79 | export(params) 80 | export(token_usage) 81 | export(tool) 82 | export(tool_annotations) 83 | export(tool_reject) 84 | export(type_array) 85 | export(type_boolean) 86 | export(type_enum) 87 | export(type_from_schema) 88 | export(type_integer) 89 | export(type_number) 90 | export(type_object) 91 | export(type_string) 92 | if (getRversion() < "4.3.0") importFrom("S7", "@") 93 | import(S7) 94 | import(httr2) 95 | import(rlang) 96 | importFrom(R6,R6Class) 97 | importFrom(lifecycle,deprecated) 98 | -------------------------------------------------------------------------------- /R/as-json.R: -------------------------------------------------------------------------------- 1 | #' @include provider.R 2 | #' @include types.R 3 | 4 | method(as_json, list(Provider, TypeBasic)) <- function(provider, x) { 5 | list(type = x@type, description = x@description %||% "") 6 | } 7 | 8 | method(as_json, list(Provider, TypeEnum)) <- function(provider, x) { 9 | list( 10 | type = "string", 11 | description = x@description %||% "", 12 | enum = as.list(x@values) 13 | ) 14 | } 15 | 16 | method(as_json, list(Provider, TypeObject)) <- function(provider, x) { 17 | names <- names2(x@properties) 18 | required <- map_lgl(x@properties, function(prop) prop@required) 19 | 20 | properties <- as_json(provider, x@properties) 21 | names(properties) <- names 22 | 23 | list( 24 | type = "object", 25 | description = x@description %||% "", 26 | properties = properties, 27 | required = as.list(names[required]), 28 | additionalProperties = x@additional_properties 29 | ) 30 | } 31 | 32 | method(as_json, list(Provider, TypeArray)) <- function(provider, x) { 33 | list( 34 | type = "array", 35 | description = x@description %||% "", 36 | items = as_json(provider, x@items) 37 | ) 38 | } 39 | 40 | method(as_json, list(Provider, TypeJsonSchema)) <- function(provider, x) { 41 | x@json 42 | } 43 | -------------------------------------------------------------------------------- /R/chat-structured.R: -------------------------------------------------------------------------------- 1 | extract_data <- function(turn, type, convert = TRUE, needs_wrapper = FALSE) { 2 | is_json <- map_lgl(turn@contents, S7_inherits, ContentJson) 3 | n <- sum(is_json) 4 | if (n != 1) { 5 | cli::cli_abort("Data extraction failed: {n} data results recieved.") 6 | } 7 | 8 | json <- turn@contents[[which(is_json)]] 9 | out <- json@value 10 | 11 | if (needs_wrapper) { 12 | out <- out$wrapper 13 | type <- type@properties[[1]] 14 | } 15 | if (convert) { 16 | out <- convert_from_type(out, type) 17 | } 18 | out 19 | } 20 | 21 | wrap_type_if_needed <- function(type, needs_wrapper = FALSE) { 22 | if (needs_wrapper) { 23 | type_object(wrapper = type) 24 | } else { 25 | type 26 | } 27 | } 28 | 29 | convert_from_type <- function(x, type) { 30 | if (is.null(x) && !type@required) { 31 | x 32 | } else if (S7_inherits(type, TypeArray)) { 33 | if (S7_inherits(type@items, TypeBasic)) { 34 | if (!type@items@required) { 35 | is_null <- map_lgl(x, is.null) 36 | if (type@items@type == "string") { 37 | x[is_null] <- list(NA_character_) 38 | } else { 39 | x[is_null] <- list(NA) 40 | } 41 | } 42 | 43 | switch( 44 | type@items@type, 45 | boolean = as.logical(x), 46 | integer = as.integer(x), 47 | number = as.numeric(x), 48 | string = as.character(x), 49 | cli::cli_abort("Unknown type {type@items@type}", .internal = TRUE) 50 | ) 51 | } else if (S7_inherits(type@items, TypeArray)) { 52 | lapply(x, function(y) convert_from_type(y, type@items)) 53 | } else if (S7_inherits(type@items, TypeEnum)) { 54 | factor(as.character(x), levels = type@items@values) 55 | } else if (S7_inherits(type@items, TypeObject)) { 56 | cols <- lapply(names(type@items@properties), function(name) { 57 | vals <- lapply(x, function(y) y[[name]]) 58 | convert_from_type( 59 | vals, 60 | type_array(items = type@items@properties[[name]]) 61 | ) 62 | }) 63 | names(cols) <- names(type@items@properties) 64 | list2DF(cols) 65 | } else { 66 | x 67 | } 68 | } else if (S7_inherits(type, TypeObject)) { 69 | out <- lapply(names(type@properties), function(name) { 70 | convert_from_type(x[[name]], type@properties[[name]]) 71 | }) 72 | set_names(out, names(type@properties)) 73 | } else if (S7_inherits(type, TypeBasic)) { 74 | if (is.null(x)) { 75 | switch( 76 | type@type, 77 | boolean = NA, 78 | integer = NA_integer_, 79 | number = NA_real_, 80 | string = NA_character_, 81 | cli::cli_abort("Unknown type {type@type}", .internal = TRUE) 82 | ) 83 | } else { 84 | x 85 | } 86 | } else { 87 | x 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /R/content-pdf.R: -------------------------------------------------------------------------------- 1 | #' Encode PDFs content for chat input 2 | #' 3 | #' @description 4 | #' These functions are used to prepare PDFs as input to the chatbot. The 5 | #' `content_pdf_url()` function is used to provide a URL to an PDF file, 6 | #' while `content_pdf_file()` is used to for local PDF files. 7 | #' 8 | #' Not all providers support PDF input, so check the documentation for the 9 | #' provider you are using. 10 | #' 11 | #' @param path,url Path or URL to a PDF file. 12 | #' @return A `ContentPDF` object 13 | #' @export 14 | content_pdf_file <- function(path) { 15 | check_string(path, allow_empty = FALSE) 16 | if (!file.exists(path) || dir.exists(path)) { 17 | cli::cli_abort("{.arg path} must be an existing file.") 18 | } 19 | 20 | ContentPDF( 21 | type = "application/pdf", 22 | data = base64_enc(path = path) 23 | ) 24 | } 25 | 26 | #' @rdname content_pdf_file 27 | #' @export 28 | content_pdf_url <- function(url) { 29 | if (grepl("^data:", url)) { 30 | parsed <- parse_data_url(url) 31 | ContentPDF(parsed$content_type, parsed$base64) 32 | } else { 33 | # TODO: need seperate ContentPDFRemote type so we can use file upload 34 | # apis where they exist. Might need some kind of mutable state so can 35 | # record point to uploaded file. 36 | path <- tempfile(fileext = ".pdf") 37 | on.exit(unlink(path)) 38 | 39 | resp <- httr2::req_perform(httr2::request(url), path = path) 40 | content_pdf_file(path) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /R/deprecated.R: -------------------------------------------------------------------------------- 1 | #' Deprecated functions 2 | #' 3 | #' @description 4 | #' `r lifecycle::badge("deprecated")` 5 | #' 6 | #' ## Deprecated in v0.2.0 7 | #' 8 | #' * [chat_azure()] was renamed to [chat_azure_openai()]. 9 | #' * [chat_bedrock()] was renamed to [chat_aws_bedrock()]. 10 | #' * [chat_claude()] was renamed to [chat_anthropic()]. 11 | #' * [chat_gemini()] was renamed to [chat_google_gemini()]. 12 | #' 13 | #' ## Deprecated in v0.1.1 14 | #' 15 | #' * [chat_cortex()] was renamed in v0.1.1 to [chat_cortex_analyst()] to 16 | #' distinguish it from the more general-purpose Snowflake Cortex chat 17 | #' function, [chat_snowflake()]. 18 | #' 19 | #' @param ... Additional arguments passed from the deprecated function to its 20 | #' replacement. 21 | #' 22 | #' @keywords internal 23 | #' @name deprecated 24 | NULL 25 | 26 | 27 | # Deprecated in v0.1.1 ----------------------------------------------------- 28 | 29 | #' @rdname deprecated 30 | #' @export 31 | chat_cortex <- function(...) { 32 | lifecycle::deprecate_warn("0.1.1", "chat_cortex()", "chat_cortex_analyst()") 33 | chat_cortex_analyst(...) 34 | } 35 | 36 | # Deprecated in v0.2.0 ----------------------------------------------------- 37 | 38 | #' @rdname deprecated 39 | #' @export 40 | chat_azure <- function(...) { 41 | lifecycle::deprecate_warn("0.2.0", "chat_azure()", "chat_azure_openai()") 42 | chat_azure_openai(...) 43 | } 44 | 45 | #' @rdname deprecated 46 | #' @export 47 | chat_bedrock <- function(...) { 48 | lifecycle::deprecate_warn("0.2.0", "chat_bedrock()", "chat_aws_bedrock()") 49 | chat_aws_bedrock(...) 50 | } 51 | 52 | #' @rdname deprecated 53 | #' @export 54 | chat_claude <- function(...) { 55 | lifecycle::deprecate_warn("0.2.0", "chat_claude()", "chat_anthropic()") 56 | chat_anthropic(...) 57 | } 58 | 59 | #' @rdname deprecated 60 | #' @export 61 | chat_gemini <- function(...) { 62 | lifecycle::deprecate_warn("0.2.0", "chat_gemini()", "chat_google_gemini()") 63 | chat_google_gemini(...) 64 | } 65 | -------------------------------------------------------------------------------- /R/ellmer-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @importFrom R6 R6Class 3 | "_PACKAGE" 4 | 5 | the <- new_environment() 6 | the$credentials_cache <- new_environment() 7 | 8 | silence_r_cmd_check_note <- function() { 9 | later::later() 10 | } 11 | 12 | ## usethis namespace: start 13 | #' @import httr2 14 | #' @import rlang 15 | #' @import S7 16 | #' @importFrom lifecycle deprecated 17 | ## usethis namespace: end 18 | NULL 19 | -------------------------------------------------------------------------------- /R/params.R: -------------------------------------------------------------------------------- 1 | #' Standard model parameters 2 | #' 3 | #' @description 4 | #' This helper function makes it easier to create a list of parameters used 5 | #' across many models. The parameter names are automatically standardised and 6 | #' included in the correctly place in the API call. 7 | #' 8 | #' Note that parameters that are not supported by a given provider will generate 9 | #' a warning, not an error. This allows you to use the same set of parameters 10 | #' across multiple providers. 11 | #' 12 | #' @param temperature Temperature of the sampling distribution. 13 | #' @param top_p The cumulative probability for token selection. 14 | #' @param top_k The number of highest probability vocabulary tokens to keep. 15 | #' @param frequency_penalty Frequency penalty for generated tokens. 16 | #' @param presence_penalty Presence penalty for generated tokens. 17 | #' @param seed Seed for random number generator. 18 | #' @param max_tokens Maximum number of tokens to generate. 19 | #' @param log_probs Include the log probabilities in the output? 20 | #' @param stop_sequences A character vector of tokens to stop generation on. 21 | #' @param ... Additional named parameters to send to the provider. 22 | #' @export 23 | params <- function( 24 | temperature = NULL, 25 | top_p = NULL, 26 | top_k = NULL, 27 | frequency_penalty = NULL, 28 | presence_penalty = NULL, 29 | seed = NULL, 30 | max_tokens = NULL, 31 | log_probs = NULL, 32 | stop_sequences = NULL, 33 | ... 34 | ) { 35 | check_number_decimal(temperature, allow_null = TRUE, min = 0) 36 | check_number_decimal(top_p, allow_null = TRUE, min = 0) 37 | check_number_whole(top_k, allow_null = TRUE, min = 0) 38 | check_number_decimal(frequency_penalty, allow_null = TRUE) 39 | check_number_decimal(presence_penalty, allow_null = TRUE) 40 | check_number_whole(seed, allow_null = TRUE) 41 | check_number_whole(max_tokens, allow_null = TRUE, min = 1) 42 | check_bool(log_probs, allow_null = TRUE) 43 | check_character(stop_sequences, allow_null = TRUE) 44 | 45 | compact(list2( 46 | temperature = temperature, 47 | top_p = top_p, 48 | top_k = top_k, 49 | frequency_penalty = frequency_penalty, 50 | presence_penalty = presence_penalty, 51 | seed = seed, 52 | max_tokens = max_tokens, 53 | log_probs = log_probs, 54 | stop_sequences = stop_sequences, 55 | extra_args = list2(...) 56 | )) 57 | } 58 | 59 | standardise_params <- function(params, provider_params) { 60 | standard <- params[names(params) != "extra_args"] 61 | 62 | unknown <- setdiff(names(standard), provider_params) 63 | if (length(unknown) > 0) { 64 | cli::cli_warn("Ignoring unsupported parameters: {.str {unknown}}") 65 | standard <- standard[names(standard) %in% provider_params] 66 | } 67 | 68 | names(standard) <- names(provider_params)[match( 69 | names(standard), 70 | provider_params 71 | )] 72 | 73 | c(standard, params$extra_args) 74 | } 75 | -------------------------------------------------------------------------------- /R/provider-deepseek.R: -------------------------------------------------------------------------------- 1 | #' @include provider-openai.R 2 | NULL 3 | 4 | #' Chat with a model hosted on DeepSeek 5 | #' 6 | #' @description 7 | #' Sign up at . 8 | #' 9 | #' ## Known limitations 10 | #' 11 | #' * Structured data extraction is not supported. 12 | #' * Images are not supported. 13 | #' 14 | #' @export 15 | #' @family chatbots 16 | #' @inheritParams chat_openai 17 | #' @param api_key `r api_key_param("DEEPSEEK_API_KEY")` 18 | #' @param base_url The base URL to the endpoint; the default uses DeepSeek. 19 | #' @param model `r param_model("deepseek-chat")` 20 | #' @inherit chat_openai return 21 | #' @examples 22 | #' \dontrun{ 23 | #' chat <- chat_deepseek() 24 | #' chat$chat("Tell me three jokes about statisticians") 25 | #' } 26 | chat_deepseek <- function( 27 | system_prompt = NULL, 28 | base_url = "https://api.deepseek.com", 29 | api_key = deepseek_key(), 30 | model = NULL, 31 | seed = NULL, 32 | api_args = list(), 33 | echo = NULL 34 | ) { 35 | model <- set_default(model, "deepseek-chat") 36 | echo <- check_echo(echo) 37 | 38 | if (is_testing() && is.null(seed)) { 39 | seed <- seed %||% 1014 40 | } 41 | 42 | provider <- ProviderDeepSeek( 43 | name = "DeepSeek", 44 | base_url = base_url, 45 | model = model, 46 | seed = seed, 47 | extra_args = api_args, 48 | api_key = api_key 49 | ) 50 | Chat$new(provider = provider, system_prompt = system_prompt, echo = echo) 51 | } 52 | 53 | ProviderDeepSeek <- new_class("ProviderDeepSeek", parent = ProviderOpenAI) 54 | 55 | method(as_json, list(ProviderDeepSeek, ContentText)) <- function(provider, x) { 56 | x@text 57 | } 58 | 59 | method(as_json, list(ProviderDeepSeek, Turn)) <- function(provider, x) { 60 | if (x@role == "user") { 61 | # Text and tool results go in separate messages 62 | texts <- keep(x@contents, S7_inherits, ContentText) 63 | texts_out <- lapply(texts, function(text) { 64 | list(role = "user", content = as_json(provider, text)) 65 | }) 66 | 67 | tools <- keep(x@contents, S7_inherits, ContentToolResult) 68 | tools_out <- lapply(tools, function(tool) { 69 | list( 70 | role = "tool", 71 | content = tool_string(tool), 72 | tool_call_id = tool@request@id 73 | ) 74 | }) 75 | 76 | c(texts_out, tools_out) 77 | } else if (x@role == "assistant") { 78 | # Tool requests come out of content and go into own argument 79 | text <- detect(x@contents, S7_inherits, ContentText) 80 | tools <- keep(x@contents, is_tool_request) 81 | 82 | list(compact(list( 83 | role = "assistant", 84 | content = as_json(provider, text), 85 | tool_calls = as_json(provider, tools) 86 | ))) 87 | } else { 88 | as_json(super(provider, ProviderOpenAI), x) 89 | } 90 | } 91 | 92 | deepseek_key <- function() key_get("DEEPSEEK_API_KEY") 93 | -------------------------------------------------------------------------------- /R/provider-github.R: -------------------------------------------------------------------------------- 1 | #' Chat with a model hosted on the GitHub model marketplace 2 | #' 3 | #' @description 4 | #' GitHub (via Azure) hosts a number of open source and OpenAI models. 5 | #' To access the GitHub model marketplace, you will need to apply for and 6 | #' be accepted into the beta access program. See 7 | #' for details. 8 | #' 9 | #' This function is a lightweight wrapper around [chat_openai()] with 10 | #' the defaults tweaked for the GitHub model marketplace. 11 | #' 12 | #' @family chatbots 13 | #' @param api_key The API key to use for authentication. You generally should 14 | #' not supply this directly, but instead manage your GitHub credentials 15 | #' as described in . 16 | #' For headless environments, this will also look in the `GITHUB_PAT` 17 | #' env var. 18 | #' @param model `r param_model("gpt-4o")` 19 | #' @export 20 | #' @inheritParams chat_openai 21 | #' @inherit chat_openai return 22 | #' @examples 23 | #' \dontrun{ 24 | #' chat <- chat_github() 25 | #' chat$chat("Tell me three jokes about statisticians") 26 | #' } 27 | chat_github <- function( 28 | system_prompt = NULL, 29 | base_url = "https://models.inference.ai.azure.com/", 30 | api_key = github_key(), 31 | model = NULL, 32 | seed = NULL, 33 | api_args = list(), 34 | echo = NULL 35 | ) { 36 | check_installed("gitcreds") 37 | 38 | model <- set_default(model, "gpt-4o") 39 | echo <- check_echo(echo) 40 | 41 | chat_openai( 42 | system_prompt = system_prompt, 43 | base_url = base_url, 44 | api_key = api_key, 45 | model = model, 46 | seed = seed, 47 | api_args = api_args, 48 | echo = echo 49 | ) 50 | } 51 | 52 | github_key <- function() { 53 | withCallingHandlers( 54 | gitcreds::gitcreds_get()$password, 55 | error = function(cnd) { 56 | cli::cli_abort( 57 | "Failed to find git credentials or GITHUB_PAT env var", 58 | parent = cnd 59 | ) 60 | } 61 | ) 62 | } 63 | 64 | #' @rdname chat_github 65 | #' @export 66 | models_github <- function( 67 | base_url = "https://models.inference.ai.azure.com/", 68 | api_key = github_key() 69 | ) { 70 | provider <- ProviderOpenAI( 71 | name = "github", 72 | model = "", 73 | base_url = base_url, 74 | api_key = api_key 75 | ) 76 | 77 | req <- base_request(provider) 78 | req <- req_url_path_append(req, "/models") 79 | resp <- req_perform(req) 80 | 81 | json <- resp_body_json(resp) 82 | 83 | id <- map_chr(json, "[[", "name") 84 | publisher <- map_chr(json, "[[", "publisher") 85 | license <- map_chr(json, "[[", "license") 86 | task <- map_chr(json, "[[", "task") 87 | 88 | data.frame( 89 | id = id, 90 | publisher = publisher, 91 | license = license, 92 | task = task 93 | ) 94 | } 95 | -------------------------------------------------------------------------------- /R/provider-perplexity.R: -------------------------------------------------------------------------------- 1 | #' Chat with a model hosted on perplexity.ai 2 | #' 3 | #' @description 4 | #' Sign up at . 5 | #' 6 | #' Perplexity AI is a platform for running LLMs that are capable of 7 | #' searching the web in real-time to help them answer questions with 8 | #' information that may not have been available when the model was 9 | #' trained. 10 | #' 11 | #' This function is a lightweight wrapper around [chat_openai()] with 12 | #' the defaults tweaked for Perplexity AI. 13 | #' 14 | #' @export 15 | #' @family chatbots 16 | #' @param api_key `r api_key_param("PERPLEXITY_API_KEY")` 17 | #' @param model `r param_model("llama-3.1-sonar-small-128k-online")` 18 | #' @inheritParams chat_openai 19 | #' @inherit chat_openai return 20 | #' @examples 21 | #' \dontrun{ 22 | #' chat <- chat_perplexity() 23 | #' chat$chat("Tell me three jokes about statisticians") 24 | #' } 25 | chat_perplexity <- function( 26 | system_prompt = NULL, 27 | base_url = "https://api.perplexity.ai/", 28 | api_key = perplexity_key(), 29 | model = NULL, 30 | seed = NULL, 31 | api_args = list(), 32 | echo = NULL 33 | ) { 34 | model <- set_default(model, "llama-3.1-sonar-small-128k-online") 35 | 36 | chat_openai( 37 | system_prompt = system_prompt, 38 | base_url = base_url, 39 | api_key = api_key, 40 | model = model, 41 | seed = seed, 42 | api_args = api_args, 43 | echo = echo 44 | ) 45 | } 46 | 47 | perplexity_key <- function() { 48 | key_get("PERPLEXITY_API_KEY") 49 | } 50 | -------------------------------------------------------------------------------- /R/provider-vllm.R: -------------------------------------------------------------------------------- 1 | #' @include provider-openai.R 2 | #' @include content.R 3 | NULL 4 | 5 | #' Chat with a model hosted by vLLM 6 | #' 7 | #' @description 8 | #' [vLLM](https://docs.vllm.ai/en/latest/) is an open source library that 9 | #' provides an efficient and convenient LLMs model server. You can use 10 | #' `chat_vllm()` to connect to endpoints powered by vLLM. 11 | #' 12 | #' @inheritParams chat_openai 13 | #' @param api_key `r api_key_param("VLLM_API_KEY")` 14 | #' @param model `r param_model(NULL, "vllm")` 15 | #' @inherit chat_openai return 16 | #' @export 17 | #' @examples 18 | #' \dontrun{ 19 | #' chat <- chat_vllm("http://my-vllm.com") 20 | #' chat$chat("Tell me three jokes about statisticians") 21 | #' } 22 | chat_vllm <- function( 23 | base_url, 24 | system_prompt = NULL, 25 | model, 26 | seed = NULL, 27 | api_args = list(), 28 | api_key = vllm_key(), 29 | echo = NULL 30 | ) { 31 | check_string(base_url) 32 | check_string(api_key) 33 | if (missing(model)) { 34 | models <- models_vllm(base_url, api_key)$id 35 | cli::cli_abort(c( 36 | "Must specify {.arg model}.", 37 | i = "Available models: {.str {models}}." 38 | )) 39 | } 40 | echo <- check_echo(echo) 41 | 42 | provider <- ProviderVllm( 43 | name = "VLLM", 44 | base_url = base_url, 45 | model = model, 46 | seed = seed, 47 | extra_args = api_args, 48 | api_key = api_key 49 | ) 50 | Chat$new(provider = provider, system_prompt = system_prompt, echo = echo) 51 | } 52 | 53 | chat_vllm_test <- function(...) { 54 | chat_vllm( 55 | base_url = "https://llm.nrp-nautilus.io/", 56 | ..., 57 | model = "llama3" 58 | ) 59 | } 60 | 61 | ProviderVllm <- new_class( 62 | "ProviderVllm", 63 | parent = ProviderOpenAI, 64 | package = "ellmer", 65 | ) 66 | 67 | # Just like OpenAI but no strict 68 | method(as_json, list(ProviderVllm, ToolDef)) <- function(provider, x) { 69 | list( 70 | type = "function", 71 | "function" = compact(list( 72 | name = x@name, 73 | description = x@description, 74 | parameters = as_json(provider, x@arguments) 75 | )) 76 | ) 77 | } 78 | 79 | vllm_key <- function() { 80 | key_get("VLLM_API_KEY") 81 | } 82 | 83 | #' @export 84 | #' @rdname chat_vllm 85 | models_vllm <- function(base_url, api_key = vllm_key()) { 86 | req <- request(base_url) 87 | req <- req_auth_bearer_token(req, api_key) 88 | req <- req_url_path(req, "/v1/models") 89 | resp <- req_perform(req) 90 | json <- resp_body_json(resp) 91 | 92 | data.frame( 93 | id = map_chr(json$data, "[[", "id") 94 | # Not accurate? 95 | # created = .POSIXct(map_dbl(json$data, "[[", "created")), 96 | # owned_by = map_chr(json$data, "[[", "owned_by") 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /R/shiny.R: -------------------------------------------------------------------------------- 1 | #' Open a live chat application 2 | #' 3 | #' @description 4 | #' 5 | #' * `live_console()` lets you chat interactively in the console. 6 | #' * `live_browser()` lets you chat interactively in a browser. 7 | #' 8 | #' Note that these functions will mutate the input `chat` object as 9 | #' you chat because your turns will be appended to the history. 10 | #' 11 | #' @param chat A chat object created by [chat_openai()] or friends. 12 | #' @param quiet If `TRUE`, suppresses the initial message that explains how 13 | #' to use the console. 14 | #' @export 15 | #' @returns (Invisibly) The input `chat`. 16 | #' @examples 17 | #' \dontrun{ 18 | #' chat <- chat_anthropic() 19 | #' live_console(chat) 20 | #' live_browser(chat) 21 | #' } 22 | live_console <- function(chat, quiet = FALSE) { 23 | if (!is_interactive()) { 24 | cli::cli_abort("The chat console is only available in interactive mode.") 25 | } 26 | 27 | if (!isTRUE(quiet)) { 28 | cli::cat_boxx( 29 | c( 30 | "Entering chat console.", 31 | "Use \"\"\" for multi-line input.", 32 | "Type 'Q' to quit." 33 | ), 34 | padding = c(0, 1, 0, 1), 35 | border_style = "double" 36 | ) 37 | } 38 | 39 | repeat { 40 | user_input <- readline(prompt = ">>> ") 41 | 42 | if (user_input == "Q") { 43 | break 44 | } 45 | 46 | if (!grepl("\\S", user_input)) { 47 | next 48 | } 49 | 50 | if (grepl('^\\s*"""', user_input)) { 51 | repeat { 52 | next_input <- readline(prompt = '... ') 53 | user_input <- paste0(user_input, "\n", next_input) 54 | if (grepl('"""\\s*$', next_input)) { 55 | break 56 | } 57 | } 58 | # Strip leading and trailing """, using regex 59 | user_input <- gsub('^\\s*"""\\s*', '', user_input) 60 | user_input <- gsub('\\s*"""\\s*$', '', user_input) 61 | } 62 | 63 | # Process the input using the provided LLM function 64 | chat$chat(user_input, echo = TRUE) 65 | cat("\n") 66 | } 67 | 68 | invisible(chat) 69 | } 70 | 71 | #' @export 72 | #' @rdname live_console 73 | live_browser <- function(chat, quiet = FALSE) { 74 | check_installed("shiny") 75 | check_installed("shinychat", version = "0.1.1.9001") 76 | 77 | if (!isTRUE(quiet)) { 78 | cli::cat_boxx( 79 | c("Entering interactive chat", "Press Ctrl+C to quit."), 80 | padding = c(0, 1, 0, 1), 81 | border_style = "double" 82 | ) 83 | } 84 | 85 | tryCatch( 86 | shiny::runGadget(shinychat::chat_app(chat, options = list(quiet = TRUE))), 87 | interrupt = function(cnd) NULL 88 | ) 89 | invisible(chat) 90 | } 91 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/R/sysdata.rda -------------------------------------------------------------------------------- /R/tokens.R: -------------------------------------------------------------------------------- 1 | on_load( 2 | the$tokens <- tokens_row(character(), character(), numeric(), numeric()) 3 | ) 4 | 5 | tokens_log <- function(provider, input = NULL, output = NULL) { 6 | input <- input %||% 0 7 | output <- output %||% 0 8 | 9 | model <- standardise_model(provider, provider@model) 10 | 11 | name <- function(provider, model) paste0(provider, "/", model) 12 | i <- tokens_match(provider@name, model, the$tokens$provider, the$tokens$model) 13 | 14 | if (is.na(i)) { 15 | new_row <- tokens_row(provider@name, model, input, output) 16 | the$tokens <- rbind(the$tokens, new_row) 17 | } else { 18 | the$tokens$input[i] <- the$tokens$input[i] + input 19 | the$tokens$output[i] <- the$tokens$output[i] + output 20 | } 21 | 22 | # Returns value to be passed to Turn 23 | c(input, output) 24 | } 25 | 26 | tokens_row <- function(provider, model, input, output) { 27 | data.frame(provider = provider, model = model, input = input, output = output) 28 | } 29 | 30 | tokens_match <- function( 31 | provider_needle, 32 | model_needle, 33 | provider_haystack, 34 | model_haystack 35 | ) { 36 | match( 37 | paste0(provider_needle, "/", model_needle), 38 | paste0(provider_haystack, "/", model_haystack) 39 | ) 40 | } 41 | 42 | 43 | local_tokens <- function(frame = parent.frame()) { 44 | old <- the$tokens 45 | the$tokens <- tokens_row(character(), character(), numeric(), numeric()) 46 | 47 | defer(the$tokens <- old, env = frame) 48 | } 49 | 50 | #' Report on token usage in the current session 51 | #' 52 | #' Call this function to find out the cumulative number of tokens that you 53 | #' have sent and recieved in the current session. The price will be shown 54 | #' if known. 55 | #' 56 | #' @export 57 | #' @return A data frame 58 | #' @examples 59 | #' token_usage() 60 | token_usage <- function() { 61 | if (nrow(the$tokens) == 0) { 62 | cli::cli_inform(c(x = "No recorded usage in this session")) 63 | return(invisible(the$tokens)) 64 | } 65 | 66 | out <- the$tokens 67 | out$price <- get_token_cost(out$provider, out$model, out$input, out$output) 68 | out 69 | } 70 | 71 | # Cost ---------------------------------------------------------------------- 72 | 73 | get_token_cost <- function(provider, model, input, output) { 74 | idx <- tokens_match(provider, model, prices$provider, prices$model) 75 | 76 | input_price <- input * prices$input[idx] / 1e6 77 | output_price <- output * prices$output[idx] / 1e6 78 | dollars(input_price + output_price) 79 | } 80 | 81 | dollars <- function(x) { 82 | structure(x, class = c("ellmer_dollars", "numeric")) 83 | } 84 | #' @export 85 | format.ellmer_dollars <- function(x, ...) { 86 | paste0(ifelse(is.na(x), "", "$"), format(unclass(round(x, 2)), nsmall = 2)) 87 | } 88 | #' @export 89 | print.ellmer_dollars <- function(x, ...) { 90 | print(format(x), quote = FALSE) 91 | invisible(x) 92 | } 93 | -------------------------------------------------------------------------------- /R/utils-merge.R: -------------------------------------------------------------------------------- 1 | # Translated from 2 | # https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/utils/_merge.py 3 | merge_dicts <- function(left, right) { 4 | for (right_k in names(right)) { 5 | right_v <- right[[right_k]] 6 | left_v <- left[[right_k]] 7 | 8 | if (is.null(right_v)) { 9 | if (!has_name(left, right_k)) { 10 | left[right_k] <- list(NULL) 11 | } 12 | } else if (is.null(left_v)) { 13 | left[[right_k]] <- right_v 14 | } else if (identical(left_v, right_v)) { 15 | next 16 | } else if (is.character(left_v)) { 17 | left[[right_k]] <- paste0(left_v, right_v) 18 | } else if (is.integer(left_v)) { 19 | left[[right_k]] <- right_v 20 | } else if (is.list(left_v)) { 21 | if (!is.null(names(right_v))) { 22 | left[[right_k]] <- merge_dicts(left_v, right_v) 23 | } else { 24 | left[[right_k]] <- merge_lists(left_v, right_v) 25 | } 26 | } else if (!identical(class(left_v), class(right_v))) { 27 | stop(paste0( 28 | "additional_kwargs['", 29 | right_k, 30 | "'] already exists in this message, but with a different type." 31 | )) 32 | } else { 33 | stop(paste0( 34 | "Additional kwargs key ", 35 | right_k, 36 | " already exists in left dict and value has unsupported type ", 37 | class(left[[right_k]]), 38 | "." 39 | )) 40 | } 41 | } 42 | 43 | left 44 | } 45 | 46 | merge_lists <- function(left, right) { 47 | if (is.null(right)) { 48 | return(left) 49 | } else if (is.null(left)) { 50 | return(right) 51 | } 52 | 53 | for (e in right) { 54 | idx <- find_index(left, e) 55 | if (is.na(idx)) { 56 | left <- c(left, list(e)) 57 | } else { 58 | # If a top-level "type" has been set for a chunk, it should no 59 | # longer be overridden by the "type" field in future chunks. 60 | if (!is.null(left[[idx]]$type) && !is.null(e$type)) { 61 | e$type <- NULL 62 | } 63 | left[[idx]] <- merge_dicts(left[[idx]], e) 64 | } 65 | } 66 | left 67 | } 68 | 69 | find_index <- function(left, e_right) { 70 | if ( 71 | !is.list(e_right) || 72 | !has_name(e_right, "index") || 73 | !is.numeric(e_right$index) 74 | ) { 75 | return(NA) 76 | } 77 | 78 | matches_idx <- map_lgl(left, function(e_left) e_left$index == e_right$index) 79 | if (sum(matches_idx) != 1) { 80 | return(NA) 81 | } 82 | which(matches_idx)[[1]] 83 | } 84 | -------------------------------------------------------------------------------- /R/utils-prettytime.R: -------------------------------------------------------------------------------- 1 | parse_ms <- function(ms) { 2 | stopifnot(is.numeric(ms)) 3 | 4 | data.frame( 5 | days = floor(ms / 86400000), 6 | hours = floor((ms / 3600000) %% 24), 7 | minutes = floor((ms / 60000) %% 60), 8 | seconds = round((ms / 1000) %% 60, 1) 9 | ) 10 | } 11 | 12 | first_positive <- function(x) which(x > 0)[1] 13 | 14 | trim <- function(x) gsub("^\\s+|\\s+$", "", x) 15 | 16 | pretty_ms <- function(ms, compact = FALSE) { 17 | stopifnot(is.numeric(ms)) 18 | 19 | parsed <- t(parse_ms(ms)) 20 | 21 | if (compact) { 22 | units <- c("d", "h", "m", "s") 23 | parsed2 <- parsed 24 | parsed2[] <- paste0(parsed, units) 25 | idx <- cbind( 26 | apply(parsed, 2, first_positive), 27 | seq_len(length(ms)) 28 | ) 29 | tmp <- paste0("~", parsed2[idx]) 30 | 31 | # handle NAs 32 | tmp[is.na(parsed2[idx])] <- NA_character_ 33 | tmp 34 | } else { 35 | ## Exact for small ones 36 | exact <- paste0(ceiling(ms), "ms") 37 | exact[is.na(ms)] <- NA_character_ 38 | 39 | ## Approximate for others, in seconds 40 | merge_pieces <- function(pieces) { 41 | ## handle NAs 42 | if (all(is.na(pieces))) { 43 | return(NA_character_) 44 | } 45 | 46 | ## handle non-NAs 47 | paste0( 48 | if (pieces[1]) paste0(pieces[1], "d "), 49 | if (pieces[2]) paste0(pieces[2], "h "), 50 | if (pieces[3]) paste0(pieces[3], "m "), 51 | if (pieces[4]) paste0(pieces[4], "s ") 52 | ) 53 | } 54 | approx <- trim(apply(parsed, 2, merge_pieces)) 55 | 56 | ifelse(ms < 1000, exact, approx) 57 | } 58 | } 59 | 60 | pretty_sec <- function(sec, compact = FALSE) { 61 | pretty_ms(sec * 1000, compact = compact) 62 | } 63 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname) { 2 | run_on_load() 3 | S7::methods_register() 4 | } 5 | 6 | # Work around S7 bug 7 | rm(format) 8 | rm(print) 9 | 10 | # enable usage of @name in package code 11 | #' @rawNamespace if (getRversion() < "4.3.0") importFrom("S7", "@") 12 | NULL 13 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://ellmer.tidyverse.org 2 | template: 3 | bootstrap: 5 4 | package: tidytemplate 5 | includes: 6 | in_header: | 7 | 8 | 9 | development: 10 | mode: auto 11 | 12 | reference: 13 | - title: Chatbots 14 | desc: > 15 | ellmer provides a simple interface to a wide range of LLM providers. Use 16 | the `chat_` functions to initialize a `Chat` object for a specific 17 | provider and model. Once created, use the methods of the `Chat` object 18 | to send messages, receive responses, manage tools and extract structured 19 | data. 20 | contents: 21 | - Chat 22 | - starts_with("chat_") 23 | - token_usage 24 | 25 | - title: Chat helpers 26 | contents: 27 | - create_tool_def 28 | - content_image_url 29 | - content_pdf_url 30 | - starts_with("live_") 31 | - interpolate 32 | - google_upload 33 | 34 | - title: Parallel and batch chat 35 | contents: 36 | - batch_chat 37 | - parallel_chat 38 | 39 | - title: Tools and structured data 40 | contents: 41 | - tool 42 | - tool_annotations 43 | - tool_reject 44 | - type_boolean 45 | 46 | - title: Objects 47 | desc: > 48 | These classes abstract away behaviour differences in chat providers so 49 | that for typical ellmer use you don't need to worry about them. You'll need 50 | to learn more about the objects if you're doing something that's only 51 | supported by one provider, or if you're implementing a new provider. 52 | contents: 53 | - Provider 54 | - Chat 55 | - Turn 56 | - Content 57 | - Type 58 | 59 | - title: Utilities 60 | contents: 61 | - contents_text 62 | - params 63 | 64 | - title: Deprecated functions 65 | contents: 66 | - deprecated 67 | 68 | news: 69 | releases: 70 | - text: "Version 0.2.0" 71 | href: https://www.tidyverse.org/blog/2025/05/ellmer-0-2-0/ 72 | - text: "Version 0.1.0" 73 | href: https://posit.co/blog/announcing-ellmer/ 74 | -------------------------------------------------------------------------------- /air.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/air.toml -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | # R CMD check results 2 | 3 | 0 errors | 0 warnings | 1 note 4 | 5 | * The failing links do all appear to work from a real browser. 6 | (I wonder if we check if https://github.com/lwthiker/curl-impersonate gives 7 | fewer false positives.) 8 | 9 | ## revdepcheck results 10 | 11 | This was a minor release with no significant user facing changes so I did not check revdeps. 12 | -------------------------------------------------------------------------------- /data-raw/openai.csv: -------------------------------------------------------------------------------- 1 | model,cached_input,input,output 2 | gpt-4.5-preview,37.50,75.00,150.00 3 | gpt-4.5-preview-2025-02-27,37.50,75.00,150.00 4 | gpt-4.1,0.50,2.00,8.00 5 | gpt-4.1-mini,0.10,0.40,1.60 6 | gpt-4.1-nano,0.025,0.10,0.40 7 | gpt-4.1-2025-04-14,0.50,2.00,8.00 8 | gpt-4.1-mini-2025-04-14,0.10,0.40,1.60 9 | gpt-4.1-nano-2025-04-14,0.025,0.10,0.40 10 | gpt-4o,1.25,2.50,10.00 11 | gpt-4o-2024-11-20,1.25,2.50,10.00 12 | gpt-4o-2024-08-06,1.25,2.50,10.00 13 | gpt-4o-2024-05-13,,5.00,15.00 14 | gpt-4o-mini,0.075,0.15,0.60 15 | gpt-4o-mini-2024-07-18,0.075,0.15,0.60 16 | o1,7.50,15.00,60.00 17 | o1-2024-12-17,7.50,15.00,60.00 18 | o1-preview-2024-09-12,7.50,15.00,60.00 19 | o1-pro,,150.00,600.00 20 | o1-pro-2025-03-19,,150.00,600.00 21 | o3-mini,0.55,1.10,4.40 22 | o3-mini-2025-01-31,0.55,1.10,4.40 23 | o1-mini,0.55,1.10,4.40 24 | o1-mini-2024-09-12,0.55,1.10,4.40 25 | gpt-4o-mini-search-preview,,0.15,0.60 26 | gpt-4o-mini-search-preview-2025-03-11,,0.15,0.60 27 | gpt-4o-search-preview,,2.50,10.00 28 | gpt-4o-search-preview-2025-03-11,,2.50,10.00 29 | computer-use-preview,,3.00,12.00 30 | computer-use-preview-2025-03-11,,3.00,12.00 31 | -------------------------------------------------------------------------------- /data-raw/prices.R: -------------------------------------------------------------------------------- 1 | library(rvest) 2 | library(httr2) 3 | library(dplyr) 4 | 5 | # OpenAI ----------------------------------------------------------------------- 6 | 7 | # Can't download with httr2/rvest: {curl} doesn't support brotli encoding 8 | # Can't download with curl: as page requires javascript 9 | # Can't download with safari: saved page doesn't contain any content 10 | # So used ChatGPT with pasted HTML at 11 | # https://chatgpt.com/share/67ed88c3-3b10-8009-a4e5-b62fc99f3d26 12 | 13 | # https://platform.openai.com/docs/pricing 14 | openai <- readr::read_csv("data-raw/openai.csv") 15 | 16 | # Anthropic -------------------------------------------------------------------- 17 | 18 | # Same problem as OpenAI website so do it BY FUCKING HAND 19 | 20 | # https://www.anthropic.com/pricing 21 | 22 | # fmt: skip 23 | anthropic <- tribble( 24 | ~model, ~cached_input, ~input, ~output, 25 | "claude-opus-4",1.50,15,75, 26 | "claude-sonnet-4",0.3,3,15, 27 | "claude-3-7-sonnet",0.3,3,15, 28 | "claude-3-5-sonnet",0.3,3,15, 29 | "claude-3-5-haiku",0.08,0.80,4, 30 | "claude-3-opus",1.5,15,75, 31 | "claude-3-haiku",0.03,0.25,1.25 32 | ) 33 | 34 | # Gemini ----------------------------------------------------------------------- 35 | 36 | # fmt: skip 37 | gemini <- tribble( 38 | ~model, ~cached_input, ~input, ~output, 39 | "gemini-2.0-flash",0.025,0.10,0.40, 40 | "gemini-2.0-flash-lite",NA,0.075,0.30, 41 | "gemini-1.5-flash",NA,0.3,0.075 42 | ) 43 | 44 | prices <- bind_rows( 45 | openai |> mutate(provider = "OpenAI", .before = 1), 46 | anthropic |> mutate(provider = "Anthropic"), 47 | gemini |> mutate(provider = "Google/Gemini") 48 | ) 49 | 50 | usethis::use_data(prices, overwrite = TRUE, internal = TRUE) 51 | -------------------------------------------------------------------------------- /ellmer.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /inst/httr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/inst/httr2.png -------------------------------------------------------------------------------- /man/Provider.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider.R 3 | \name{Provider} 4 | \alias{Provider} 5 | \title{A chatbot provider} 6 | \usage{ 7 | Provider( 8 | name = stop("Required"), 9 | model = stop("Required"), 10 | base_url = stop("Required"), 11 | params = list(), 12 | extra_args = list() 13 | ) 14 | } 15 | \arguments{ 16 | \item{name}{Name of the provider.} 17 | 18 | \item{model}{Name of the model.} 19 | 20 | \item{base_url}{The base URL for the API.} 21 | 22 | \item{params}{A list of standard parameters created by \code{\link[=params]{params()}}.} 23 | 24 | \item{extra_args}{Arbitrary extra arguments to be included in the request body.} 25 | } 26 | \value{ 27 | An S7 Provider object. 28 | } 29 | \description{ 30 | A Provider captures the details of one chatbot service/API. This captures 31 | how the API works, not the details of the underlying large language model. 32 | Different providers might offer the same (open source) model behind a 33 | different API. 34 | } 35 | \details{ 36 | To add support for a new backend, you will need to subclass \code{Provider} 37 | (adding any additional fields that your provider needs) and then implement 38 | the various generics that control the behavior of each provider. 39 | } 40 | \examples{ 41 | Provider( 42 | name = "CoolModels", 43 | model = "my_model", 44 | base_url = "https://cool-models.com" 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /man/Turn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/turns.R 3 | \name{Turn} 4 | \alias{Turn} 5 | \title{A user or assistant turn} 6 | \usage{ 7 | Turn(role, contents = list(), json = list(), tokens = c(0, 0)) 8 | } 9 | \arguments{ 10 | \item{role}{Either "user", "assistant", or "system".} 11 | 12 | \item{contents}{A list of \link{Content} objects.} 13 | 14 | \item{json}{The serialized JSON corresponding to the underlying data of 15 | the turns. Currently only provided for assistant. 16 | 17 | This is useful if there's information returned by the provider that ellmer 18 | doesn't otherwise expose.} 19 | 20 | \item{tokens}{A numeric vector of length 2 representing the number of 21 | input and output tokens (respectively) used in this turn. Currently 22 | only recorded for assistant turns.} 23 | } 24 | \value{ 25 | An S7 \code{Turn} object 26 | } 27 | \description{ 28 | Every conversation with a chatbot consists of pairs of user and assistant 29 | turns, corresponding to an HTTP request and response. These turns are 30 | represented by the \code{Turn} object, which contains a list of \link{Content}s representing 31 | the individual messages within the turn. These might be text, images, tool 32 | requests (assistant only), or tool responses (user only). 33 | 34 | Note that a call to \verb{$chat()} and related functions may result in multiple 35 | user-assistant turn cycles. For example, if you have registered tools, 36 | ellmer will automatically handle the tool calling loop, which may result in 37 | any number of additional cycles. Learn more about tool calling in 38 | \code{vignette("tool-calling")}. 39 | } 40 | \examples{ 41 | Turn(role = "user", contents = list(ContentText("Hello, world!"))) 42 | } 43 | -------------------------------------------------------------------------------- /man/Type.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/types.R 3 | \name{Type} 4 | \alias{Type} 5 | \alias{TypeBasic} 6 | \alias{TypeEnum} 7 | \alias{TypeArray} 8 | \alias{TypeJsonSchema} 9 | \alias{TypeObject} 10 | \title{Type definitions for function calling and structured data extraction.} 11 | \usage{ 12 | TypeBasic(description = NULL, required = TRUE, type = stop("Required")) 13 | 14 | TypeEnum(description = NULL, required = TRUE, values = character(0)) 15 | 16 | TypeArray(description = NULL, required = TRUE, items = Type()) 17 | 18 | TypeJsonSchema(description = NULL, required = TRUE, json = list()) 19 | 20 | TypeObject( 21 | description = NULL, 22 | required = TRUE, 23 | properties = list(), 24 | additional_properties = TRUE 25 | ) 26 | } 27 | \arguments{ 28 | \item{description}{The purpose of the component. This is 29 | used by the LLM to determine what values to pass to the tool or what 30 | values to extract in the structured data, so the more detail that you can 31 | provide here, the better.} 32 | 33 | \item{required}{Is the component or argument required? 34 | 35 | In type descriptions for structured data, if \code{required = FALSE} and the 36 | component does not exist in the data, the LLM may hallucinate a value. Only 37 | applies when the element is nested inside of a \code{type_object()}. 38 | 39 | In tool definitions, \code{required = TRUE} signals that the LLM should always 40 | provide a value. Arguments with \code{required = FALSE} should have a default 41 | value in the tool function's definition. If the LLM does not provide a 42 | value, the default value will be used.} 43 | 44 | \item{type}{Basic type name. Must be one of \code{boolean}, \code{integer}, 45 | \code{number}, or \code{string}.} 46 | 47 | \item{values}{Character vector of permitted values.} 48 | 49 | \item{items}{The type of the array items. Can be created by any of the 50 | \code{type_} function.} 51 | 52 | \item{json}{A JSON schema object as a list.} 53 | 54 | \item{properties}{Named list of properties stored inside the object. 55 | Each element should be an S7 \code{Type} object.`} 56 | 57 | \item{additional_properties}{Can the object have arbitrary additional 58 | properties that are not explicitly listed? Only supported by Claude.} 59 | } 60 | \value{ 61 | S7 objects inheriting from \code{Type} 62 | } 63 | \description{ 64 | These S7 classes are provided for use by package devlopers who are 65 | extending ellmer. In every day use, use \code{\link[=type_boolean]{type_boolean()}} and friends. 66 | } 67 | \examples{ 68 | TypeBasic(type = "boolean") 69 | TypeArray(items = TypeBasic(type = "boolean")) 70 | } 71 | -------------------------------------------------------------------------------- /man/chat_cloudflare.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-cloudflare.R 3 | \name{chat_cloudflare} 4 | \alias{chat_cloudflare} 5 | \title{Chat with a model hosted on CloudFlare} 6 | \usage{ 7 | chat_cloudflare( 8 | account = cloudflare_account(), 9 | system_prompt = NULL, 10 | params = NULL, 11 | api_key = cloudflare_key(), 12 | model = NULL, 13 | api_args = list(), 14 | echo = NULL 15 | ) 16 | } 17 | \arguments{ 18 | \item{account}{The Cloudflare account ID. Taken from the 19 | \code{CLOUDFLARE_ACCOUNT_ID} env var, if defined.} 20 | 21 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 22 | 23 | \item{params}{Common model parameters, usually created by \code{\link[=params]{params()}}.} 24 | 25 | \item{api_key}{The API key to use for authentication. You generally should 26 | not supply this directly, but instead set the \code{HUGGINGFACE_API_KEY} environment 27 | variable.} 28 | 29 | \item{model}{The model to use for the chat (defaults to "meta-llama/Llama-3.3-70b-instruct-fp8-fast"). 30 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 31 | 32 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 33 | of every chat API call. Combined with the body object generated by ellmer 34 | with \code{\link[=modifyList]{modifyList()}}.} 35 | 36 | \item{echo}{One of the following options: 37 | \itemize{ 38 | \item \code{none}: don't emit any output (default when running in a function). 39 | \item \code{output}: echo text and tool-calling output as it streams in (default 40 | when running at the console). 41 | \item \code{all}: echo all input and output. 42 | } 43 | 44 | Note this only affects the \code{chat()} method.} 45 | } 46 | \value{ 47 | A \link{Chat} object. 48 | } 49 | \description{ 50 | \href{https://www.cloudflare.com/developer-platform/products/workers-ai/}{Cloudflare} 51 | works AI hosts a variety of open-source AI models. To use the Cloudflare 52 | API, you must have an Account ID and an Access Token, which you can obtain 53 | \href{https://developers.cloudflare.com/workers-ai/get-started/rest-api/}{by following these instructions}. 54 | \subsection{Known limitations}{ 55 | \itemize{ 56 | \item Tool calling does not appear to work. 57 | \item Images don't appear to work. 58 | } 59 | } 60 | } 61 | \examples{ 62 | \dontrun{ 63 | chat <- chat_cloudflare() 64 | chat$chat("Tell me three jokes about statisticians") 65 | } 66 | } 67 | \seealso{ 68 | Other chatbots: 69 | \code{\link{chat_anthropic}()}, 70 | \code{\link{chat_aws_bedrock}()}, 71 | \code{\link{chat_azure_openai}()}, 72 | \code{\link{chat_cortex_analyst}()}, 73 | \code{\link{chat_databricks}()}, 74 | \code{\link{chat_deepseek}()}, 75 | \code{\link{chat_github}()}, 76 | \code{\link{chat_google_gemini}()}, 77 | \code{\link{chat_groq}()}, 78 | \code{\link{chat_huggingface}()}, 79 | \code{\link{chat_mistral}()}, 80 | \code{\link{chat_ollama}()}, 81 | \code{\link{chat_openai}()}, 82 | \code{\link{chat_openrouter}()}, 83 | \code{\link{chat_perplexity}()}, 84 | \code{\link{chat_portkey}()} 85 | } 86 | \concept{chatbots} 87 | -------------------------------------------------------------------------------- /man/chat_deepseek.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-deepseek.R 3 | \name{chat_deepseek} 4 | \alias{chat_deepseek} 5 | \title{Chat with a model hosted on DeepSeek} 6 | \usage{ 7 | chat_deepseek( 8 | system_prompt = NULL, 9 | base_url = "https://api.deepseek.com", 10 | api_key = deepseek_key(), 11 | model = NULL, 12 | seed = NULL, 13 | api_args = list(), 14 | echo = NULL 15 | ) 16 | } 17 | \arguments{ 18 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 19 | 20 | \item{base_url}{The base URL to the endpoint; the default uses DeepSeek.} 21 | 22 | \item{api_key}{API key to use for authentication. 23 | 24 | You generally should not supply this directly, but instead set the \code{DEEPSEEK_API_KEY} environment variable. 25 | The best place to set this is in \code{.Renviron}, 26 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 27 | 28 | \item{model}{The model to use for the chat (defaults to "deepseek-chat"). 29 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 30 | 31 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 32 | more reproducible.} 33 | 34 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 35 | of every chat API call. Combined with the body object generated by ellmer 36 | with \code{\link[=modifyList]{modifyList()}}.} 37 | 38 | \item{echo}{One of the following options: 39 | \itemize{ 40 | \item \code{none}: don't emit any output (default when running in a function). 41 | \item \code{output}: echo text and tool-calling output as it streams in (default 42 | when running at the console). 43 | \item \code{all}: echo all input and output. 44 | } 45 | 46 | Note this only affects the \code{chat()} method.} 47 | } 48 | \value{ 49 | A \link{Chat} object. 50 | } 51 | \description{ 52 | Sign up at \url{https://platform.deepseek.com}. 53 | \subsection{Known limitations}{ 54 | \itemize{ 55 | \item Structured data extraction is not supported. 56 | \item Images are not supported. 57 | } 58 | } 59 | } 60 | \examples{ 61 | \dontrun{ 62 | chat <- chat_deepseek() 63 | chat$chat("Tell me three jokes about statisticians") 64 | } 65 | } 66 | \seealso{ 67 | Other chatbots: 68 | \code{\link{chat_anthropic}()}, 69 | \code{\link{chat_aws_bedrock}()}, 70 | \code{\link{chat_azure_openai}()}, 71 | \code{\link{chat_cloudflare}()}, 72 | \code{\link{chat_cortex_analyst}()}, 73 | \code{\link{chat_databricks}()}, 74 | \code{\link{chat_github}()}, 75 | \code{\link{chat_google_gemini}()}, 76 | \code{\link{chat_groq}()}, 77 | \code{\link{chat_huggingface}()}, 78 | \code{\link{chat_mistral}()}, 79 | \code{\link{chat_ollama}()}, 80 | \code{\link{chat_openai}()}, 81 | \code{\link{chat_openrouter}()}, 82 | \code{\link{chat_perplexity}()}, 83 | \code{\link{chat_portkey}()} 84 | } 85 | \concept{chatbots} 86 | -------------------------------------------------------------------------------- /man/chat_groq.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-groq.R 3 | \name{chat_groq} 4 | \alias{chat_groq} 5 | \title{Chat with a model hosted on Groq} 6 | \usage{ 7 | chat_groq( 8 | system_prompt = NULL, 9 | base_url = "https://api.groq.com/openai/v1", 10 | api_key = groq_key(), 11 | model = NULL, 12 | seed = NULL, 13 | api_args = list(), 14 | echo = NULL 15 | ) 16 | } 17 | \arguments{ 18 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 19 | 20 | \item{base_url}{The base URL to the endpoint; the default uses OpenAI.} 21 | 22 | \item{api_key}{API key to use for authentication. 23 | 24 | You generally should not supply this directly, but instead set the \code{GROQ_API_KEY} environment variable. 25 | The best place to set this is in \code{.Renviron}, 26 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 27 | 28 | \item{model}{The model to use for the chat (defaults to "llama3-8b-8192"). 29 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 30 | 31 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 32 | more reproducible.} 33 | 34 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 35 | of every chat API call. Combined with the body object generated by ellmer 36 | with \code{\link[=modifyList]{modifyList()}}.} 37 | 38 | \item{echo}{One of the following options: 39 | \itemize{ 40 | \item \code{none}: don't emit any output (default when running in a function). 41 | \item \code{output}: echo text and tool-calling output as it streams in (default 42 | when running at the console). 43 | \item \code{all}: echo all input and output. 44 | } 45 | 46 | Note this only affects the \code{chat()} method.} 47 | } 48 | \value{ 49 | A \link{Chat} object. 50 | } 51 | \description{ 52 | Sign up at \url{https://groq.com}. 53 | 54 | This function is a lightweight wrapper around \code{\link[=chat_openai]{chat_openai()}} with 55 | the defaults tweaked for groq. 56 | \subsection{Known limitations}{ 57 | 58 | groq does not currently support structured data extraction. 59 | } 60 | } 61 | \examples{ 62 | \dontrun{ 63 | chat <- chat_groq() 64 | chat$chat("Tell me three jokes about statisticians") 65 | } 66 | } 67 | \seealso{ 68 | Other chatbots: 69 | \code{\link{chat_anthropic}()}, 70 | \code{\link{chat_aws_bedrock}()}, 71 | \code{\link{chat_azure_openai}()}, 72 | \code{\link{chat_cloudflare}()}, 73 | \code{\link{chat_cortex_analyst}()}, 74 | \code{\link{chat_databricks}()}, 75 | \code{\link{chat_deepseek}()}, 76 | \code{\link{chat_github}()}, 77 | \code{\link{chat_google_gemini}()}, 78 | \code{\link{chat_huggingface}()}, 79 | \code{\link{chat_mistral}()}, 80 | \code{\link{chat_ollama}()}, 81 | \code{\link{chat_openai}()}, 82 | \code{\link{chat_openrouter}()}, 83 | \code{\link{chat_perplexity}()}, 84 | \code{\link{chat_portkey}()} 85 | } 86 | \concept{chatbots} 87 | -------------------------------------------------------------------------------- /man/chat_mistral.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-mistral.R 3 | \name{chat_mistral} 4 | \alias{chat_mistral} 5 | \title{Chat with a model hosted on Mistral's La Platforme} 6 | \usage{ 7 | chat_mistral( 8 | system_prompt = NULL, 9 | params = NULL, 10 | api_key = mistral_key(), 11 | model = NULL, 12 | seed = NULL, 13 | api_args = list(), 14 | echo = NULL 15 | ) 16 | } 17 | \arguments{ 18 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 19 | 20 | \item{params}{Common model parameters, usually created by \code{\link[=params]{params()}}.} 21 | 22 | \item{api_key}{API key to use for authentication. 23 | 24 | You generally should not supply this directly, but instead set the \code{MISTRAL_API_KEY} environment variable. 25 | The best place to set this is in \code{.Renviron}, 26 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 27 | 28 | \item{model}{The model to use for the chat (defaults to "mistral-large-latest"). 29 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 30 | 31 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 32 | more reproducible.} 33 | 34 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 35 | of every chat API call. Combined with the body object generated by ellmer 36 | with \code{\link[=modifyList]{modifyList()}}.} 37 | 38 | \item{echo}{One of the following options: 39 | \itemize{ 40 | \item \code{none}: don't emit any output (default when running in a function). 41 | \item \code{output}: echo text and tool-calling output as it streams in (default 42 | when running at the console). 43 | \item \code{all}: echo all input and output. 44 | } 45 | 46 | Note this only affects the \code{chat()} method.} 47 | } 48 | \value{ 49 | A \link{Chat} object. 50 | } 51 | \description{ 52 | Get your API key from \url{https://console.mistral.ai/api-keys}. 53 | \subsection{Known limitations}{ 54 | \itemize{ 55 | \item Tool calling is unstable. 56 | \item Images require a model that supports images. 57 | } 58 | } 59 | } 60 | \examples{ 61 | \dontrun{ 62 | chat <- chat_mistral() 63 | chat$chat("Tell me three jokes about statisticians") 64 | } 65 | } 66 | \seealso{ 67 | Other chatbots: 68 | \code{\link{chat_anthropic}()}, 69 | \code{\link{chat_aws_bedrock}()}, 70 | \code{\link{chat_azure_openai}()}, 71 | \code{\link{chat_cloudflare}()}, 72 | \code{\link{chat_cortex_analyst}()}, 73 | \code{\link{chat_databricks}()}, 74 | \code{\link{chat_deepseek}()}, 75 | \code{\link{chat_github}()}, 76 | \code{\link{chat_google_gemini}()}, 77 | \code{\link{chat_groq}()}, 78 | \code{\link{chat_huggingface}()}, 79 | \code{\link{chat_ollama}()}, 80 | \code{\link{chat_openai}()}, 81 | \code{\link{chat_openrouter}()}, 82 | \code{\link{chat_perplexity}()}, 83 | \code{\link{chat_portkey}()} 84 | } 85 | \concept{chatbots} 86 | -------------------------------------------------------------------------------- /man/chat_openrouter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-openrouter.R 3 | \name{chat_openrouter} 4 | \alias{chat_openrouter} 5 | \title{Chat with one of the many models hosted on OpenRouter} 6 | \usage{ 7 | chat_openrouter( 8 | system_prompt = NULL, 9 | api_key = openrouter_key(), 10 | model = NULL, 11 | seed = NULL, 12 | api_args = list(), 13 | echo = c("none", "output", "all") 14 | ) 15 | } 16 | \arguments{ 17 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 18 | 19 | \item{api_key}{API key to use for authentication. 20 | 21 | You generally should not supply this directly, but instead set the \code{OPENROUTER_API_KEY} environment variable. 22 | The best place to set this is in \code{.Renviron}, 23 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 24 | 25 | \item{model}{The model to use for the chat (defaults to "gpt-4o"). 26 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 27 | 28 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 29 | more reproducible.} 30 | 31 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 32 | of every chat API call. Combined with the body object generated by ellmer 33 | with \code{\link[=modifyList]{modifyList()}}.} 34 | 35 | \item{echo}{One of the following options: 36 | \itemize{ 37 | \item \code{none}: don't emit any output (default when running in a function). 38 | \item \code{output}: echo text and tool-calling output as it streams in (default 39 | when running at the console). 40 | \item \code{all}: echo all input and output. 41 | } 42 | 43 | Note this only affects the \code{chat()} method.} 44 | } 45 | \value{ 46 | A \link{Chat} object. 47 | } 48 | \description{ 49 | Sign up at \url{https://openrouter.ai}. 50 | 51 | Support for features depends on the underlying model that you use; see 52 | \url{https://openrouter.ai/models} for details. 53 | } 54 | \examples{ 55 | \dontrun{ 56 | chat <- chat_openrouter() 57 | chat$chat("Tell me three jokes about statisticians") 58 | } 59 | } 60 | \seealso{ 61 | Other chatbots: 62 | \code{\link{chat_anthropic}()}, 63 | \code{\link{chat_aws_bedrock}()}, 64 | \code{\link{chat_azure_openai}()}, 65 | \code{\link{chat_cloudflare}()}, 66 | \code{\link{chat_cortex_analyst}()}, 67 | \code{\link{chat_databricks}()}, 68 | \code{\link{chat_deepseek}()}, 69 | \code{\link{chat_github}()}, 70 | \code{\link{chat_google_gemini}()}, 71 | \code{\link{chat_groq}()}, 72 | \code{\link{chat_huggingface}()}, 73 | \code{\link{chat_mistral}()}, 74 | \code{\link{chat_ollama}()}, 75 | \code{\link{chat_openai}()}, 76 | \code{\link{chat_perplexity}()}, 77 | \code{\link{chat_portkey}()} 78 | } 79 | \concept{chatbots} 80 | -------------------------------------------------------------------------------- /man/chat_perplexity.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-perplexity.R 3 | \name{chat_perplexity} 4 | \alias{chat_perplexity} 5 | \title{Chat with a model hosted on perplexity.ai} 6 | \usage{ 7 | chat_perplexity( 8 | system_prompt = NULL, 9 | base_url = "https://api.perplexity.ai/", 10 | api_key = perplexity_key(), 11 | model = NULL, 12 | seed = NULL, 13 | api_args = list(), 14 | echo = NULL 15 | ) 16 | } 17 | \arguments{ 18 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 19 | 20 | \item{base_url}{The base URL to the endpoint; the default uses OpenAI.} 21 | 22 | \item{api_key}{API key to use for authentication. 23 | 24 | You generally should not supply this directly, but instead set the \code{PERPLEXITY_API_KEY} environment variable. 25 | The best place to set this is in \code{.Renviron}, 26 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 27 | 28 | \item{model}{The model to use for the chat (defaults to "llama-3.1-sonar-small-128k-online"). 29 | We regularly update the default, so we strongly recommend explicitly specifying a model for anything other than casual use.} 30 | 31 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 32 | more reproducible.} 33 | 34 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 35 | of every chat API call. Combined with the body object generated by ellmer 36 | with \code{\link[=modifyList]{modifyList()}}.} 37 | 38 | \item{echo}{One of the following options: 39 | \itemize{ 40 | \item \code{none}: don't emit any output (default when running in a function). 41 | \item \code{output}: echo text and tool-calling output as it streams in (default 42 | when running at the console). 43 | \item \code{all}: echo all input and output. 44 | } 45 | 46 | Note this only affects the \code{chat()} method.} 47 | } 48 | \value{ 49 | A \link{Chat} object. 50 | } 51 | \description{ 52 | Sign up at \url{https://www.perplexity.ai}. 53 | 54 | Perplexity AI is a platform for running LLMs that are capable of 55 | searching the web in real-time to help them answer questions with 56 | information that may not have been available when the model was 57 | trained. 58 | 59 | This function is a lightweight wrapper around \code{\link[=chat_openai]{chat_openai()}} with 60 | the defaults tweaked for Perplexity AI. 61 | } 62 | \examples{ 63 | \dontrun{ 64 | chat <- chat_perplexity() 65 | chat$chat("Tell me three jokes about statisticians") 66 | } 67 | } 68 | \seealso{ 69 | Other chatbots: 70 | \code{\link{chat_anthropic}()}, 71 | \code{\link{chat_aws_bedrock}()}, 72 | \code{\link{chat_azure_openai}()}, 73 | \code{\link{chat_cloudflare}()}, 74 | \code{\link{chat_cortex_analyst}()}, 75 | \code{\link{chat_databricks}()}, 76 | \code{\link{chat_deepseek}()}, 77 | \code{\link{chat_github}()}, 78 | \code{\link{chat_google_gemini}()}, 79 | \code{\link{chat_groq}()}, 80 | \code{\link{chat_huggingface}()}, 81 | \code{\link{chat_mistral}()}, 82 | \code{\link{chat_ollama}()}, 83 | \code{\link{chat_openai}()}, 84 | \code{\link{chat_openrouter}()}, 85 | \code{\link{chat_portkey}()} 86 | } 87 | \concept{chatbots} 88 | -------------------------------------------------------------------------------- /man/chat_vllm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-vllm.R 3 | \name{chat_vllm} 4 | \alias{chat_vllm} 5 | \alias{models_vllm} 6 | \title{Chat with a model hosted by vLLM} 7 | \usage{ 8 | chat_vllm( 9 | base_url, 10 | system_prompt = NULL, 11 | model, 12 | seed = NULL, 13 | api_args = list(), 14 | api_key = vllm_key(), 15 | echo = NULL 16 | ) 17 | 18 | models_vllm(base_url, api_key = vllm_key()) 19 | } 20 | \arguments{ 21 | \item{base_url}{The base URL to the endpoint; the default uses OpenAI.} 22 | 23 | \item{system_prompt}{A system prompt to set the behavior of the assistant.} 24 | 25 | \item{model}{The model to use for the chat. 26 | Use \code{models_vllm()} to see all options.} 27 | 28 | \item{seed}{Optional integer seed that ChatGPT uses to try and make output 29 | more reproducible.} 30 | 31 | \item{api_args}{Named list of arbitrary extra arguments appended to the body 32 | of every chat API call. Combined with the body object generated by ellmer 33 | with \code{\link[=modifyList]{modifyList()}}.} 34 | 35 | \item{api_key}{API key to use for authentication. 36 | 37 | You generally should not supply this directly, but instead set the \code{VLLM_API_KEY} environment variable. 38 | The best place to set this is in \code{.Renviron}, 39 | which you can easily edit by calling \code{usethis::edit_r_environ()}.} 40 | 41 | \item{echo}{One of the following options: 42 | \itemize{ 43 | \item \code{none}: don't emit any output (default when running in a function). 44 | \item \code{output}: echo text and tool-calling output as it streams in (default 45 | when running at the console). 46 | \item \code{all}: echo all input and output. 47 | } 48 | 49 | Note this only affects the \code{chat()} method.} 50 | } 51 | \value{ 52 | A \link{Chat} object. 53 | } 54 | \description{ 55 | \href{https://docs.vllm.ai/en/latest/}{vLLM} is an open source library that 56 | provides an efficient and convenient LLMs model server. You can use 57 | \code{chat_vllm()} to connect to endpoints powered by vLLM. 58 | } 59 | \examples{ 60 | \dontrun{ 61 | chat <- chat_vllm("http://my-vllm.com") 62 | chat$chat("Tell me three jokes about statisticians") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /man/content_pdf_file.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/content-pdf.R 3 | \name{content_pdf_file} 4 | \alias{content_pdf_file} 5 | \alias{content_pdf_url} 6 | \title{Encode PDFs content for chat input} 7 | \usage{ 8 | content_pdf_file(path) 9 | 10 | content_pdf_url(url) 11 | } 12 | \arguments{ 13 | \item{path, url}{Path or URL to a PDF file.} 14 | } 15 | \value{ 16 | A \code{ContentPDF} object 17 | } 18 | \description{ 19 | These functions are used to prepare PDFs as input to the chatbot. The 20 | \code{content_pdf_url()} function is used to provide a URL to an PDF file, 21 | while \code{content_pdf_file()} is used to for local PDF files. 22 | 23 | Not all providers support PDF input, so check the documentation for the 24 | provider you are using. 25 | } 26 | -------------------------------------------------------------------------------- /man/contents_text.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/content.R 3 | \name{contents_text} 4 | \alias{contents_text} 5 | \alias{contents_html} 6 | \alias{contents_markdown} 7 | \title{Format contents into a textual representation} 8 | \usage{ 9 | contents_text(content, ...) 10 | 11 | contents_html(content, ...) 12 | 13 | contents_markdown(content, ...) 14 | } 15 | \arguments{ 16 | \item{content}{The \link{Turn} or \link{Content} object to be converted into text. 17 | \code{contents_markdown()} also accepts \link{Chat} instances to turn the entire 18 | conversation history into markdown text.} 19 | 20 | \item{...}{Additional arguments passed to methods.} 21 | } 22 | \value{ 23 | A string of text, markdown or HTML. 24 | } 25 | \description{ 26 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 27 | 28 | These generic functions can be use to convert \link{Turn} contents or \link{Content} 29 | objects into textual representations. 30 | \itemize{ 31 | \item \code{contents_text()} is the most minimal and only includes \link{ContentText} 32 | objects in the output. 33 | \item \code{contents_markdown()} returns the text content (which it assumes to be 34 | markdown and does not convert it) plus markdown representations of images 35 | and other content types. 36 | \item \code{contents_html()} returns the text content, converted from markdown to 37 | HTML with \code{\link[commonmark:commonmark]{commonmark::markdown_html()}}, plus HTML representations of 38 | images and other content types. 39 | } 40 | 41 | These content types will continue to grow and change as ellmer evolves to 42 | support more providers and as providers add more content types. 43 | } 44 | \examples{ 45 | turns <- list( 46 | Turn("user", contents = list( 47 | ContentText("What's this image?"), 48 | content_image_url("https://placehold.co/200x200") 49 | )), 50 | Turn("assistant", "It's a placeholder image.") 51 | ) 52 | 53 | lapply(turns, contents_text) 54 | lapply(turns, contents_markdown) 55 | if (rlang::is_installed("commonmark")) { 56 | contents_html(turns[[1]]) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /man/create_tool_def.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tools-def-auto.R 3 | \name{create_tool_def} 4 | \alias{create_tool_def} 5 | \title{Create metadata for a tool} 6 | \usage{ 7 | create_tool_def( 8 | topic, 9 | chat = NULL, 10 | model = deprecated(), 11 | echo = interactive(), 12 | verbose = FALSE 13 | ) 14 | } 15 | \arguments{ 16 | \item{topic}{A symbol or string literal naming the function to create 17 | metadata for. Can also be an expression of the form \code{pkg::fun}.} 18 | 19 | \item{chat}{A \code{Chat} object used to generate the output. If \code{NULL} 20 | (the default) uses \code{\link[=chat_openai]{chat_openai()}}.} 21 | 22 | \item{model}{\code{lifecycle::badge("deprecated")} Formally used for definining 23 | the model used by the chat. Now supply \code{chat} instead.} 24 | 25 | \item{echo}{Emit the registration code to the console. Defaults to \code{TRUE} in 26 | interactive sessions.} 27 | 28 | \item{verbose}{If \code{TRUE}, print the input we send to the LLM, which may be 29 | useful for debugging unexpectedly poor results.} 30 | } 31 | \value{ 32 | A \code{register_tool} call that you can copy and paste into your code. 33 | Returned invisibly if \code{echo} is \code{TRUE}. 34 | } 35 | \description{ 36 | In order to use a function as a tool in a chat, you need to craft the right 37 | call to \code{\link[=tool]{tool()}}. This function helps you do that for documented functions by 38 | extracting the function's R documentation and creating a \code{tool()} call for 39 | you, using an LLM. It's meant to be used interactively while writing your 40 | code, not as part of your final code. 41 | 42 | If the function has package documentation, that will be used. Otherwise, if 43 | the source code of the function can be automatically detected, then the 44 | comments immediately preceding the function are used (especially helpful if 45 | those are Roxygen comments). If neither are available, then just the function 46 | signature is used. 47 | 48 | Note that this function is inherently imperfect. It can't handle all possible 49 | R functions, because not all parameters are suitable for use in a tool call 50 | (for example, because they're not serializable to simple JSON objects). The 51 | documentation might not specify the expected shape of arguments to the level 52 | of detail that would allow an exact JSON schema to be generated. Please be 53 | sure to review the generated code before using it! 54 | } 55 | \examples{ 56 | \dontrun{ 57 | # These are all equivalent 58 | create_tool_def(rnorm) 59 | create_tool_def(stats::rnorm) 60 | create_tool_def("rnorm") 61 | create_tool_def("rnorm", chat = chat_azure_openai()) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /man/deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{deprecated} 4 | \alias{deprecated} 5 | \alias{chat_cortex} 6 | \alias{chat_azure} 7 | \alias{chat_bedrock} 8 | \alias{chat_claude} 9 | \alias{chat_gemini} 10 | \title{Deprecated functions} 11 | \usage{ 12 | chat_cortex(...) 13 | 14 | chat_azure(...) 15 | 16 | chat_bedrock(...) 17 | 18 | chat_claude(...) 19 | 20 | chat_gemini(...) 21 | } 22 | \arguments{ 23 | \item{...}{Additional arguments passed from the deprecated function to its 24 | replacement.} 25 | } 26 | \description{ 27 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 28 | \subsection{Deprecated in v0.2.0}{ 29 | \itemize{ 30 | \item \code{\link[=chat_azure]{chat_azure()}} was renamed to \code{\link[=chat_azure_openai]{chat_azure_openai()}}. 31 | \item \code{\link[=chat_bedrock]{chat_bedrock()}} was renamed to \code{\link[=chat_aws_bedrock]{chat_aws_bedrock()}}. 32 | \item \code{\link[=chat_claude]{chat_claude()}} was renamed to \code{\link[=chat_anthropic]{chat_anthropic()}}. 33 | \item \code{\link[=chat_gemini]{chat_gemini()}} was renamed to \code{\link[=chat_google_gemini]{chat_google_gemini()}}. 34 | } 35 | } 36 | 37 | \subsection{Deprecated in v0.1.1}{ 38 | \itemize{ 39 | \item \code{\link[=chat_cortex]{chat_cortex()}} was renamed in v0.1.1 to \code{\link[=chat_cortex_analyst]{chat_cortex_analyst()}} to 40 | distinguish it from the more general-purpose Snowflake Cortex chat 41 | function, \code{\link[=chat_snowflake]{chat_snowflake()}}. 42 | } 43 | } 44 | } 45 | \keyword{internal} 46 | -------------------------------------------------------------------------------- /man/ellmer-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ellmer-package.R 3 | \docType{package} 4 | \name{ellmer-package} 5 | \alias{ellmer} 6 | \alias{ellmer-package} 7 | \title{ellmer: Chat with Large Language Models} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | Chat with large language models from a range of providers including 'Claude' \url{https://claude.ai}, 'OpenAI' \url{https://chatgpt.com}, and more. Supports streaming, asynchronous calls, tool calling, and structured data extraction. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://ellmer.tidyverse.org} 17 | \item \url{https://github.com/tidyverse/ellmer} 18 | \item Report bugs at \url{https://github.com/tidyverse/ellmer/issues} 19 | } 20 | 21 | } 22 | \author{ 23 | \strong{Maintainer}: Hadley Wickham \email{hadley@posit.co} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) 24 | 25 | Authors: 26 | \itemize{ 27 | \item Joe Cheng 28 | \item Aaron Jacobs 29 | \item Garrick Aden-Buie \email{garrick@posit.co} (\href{https://orcid.org/0000-0002-7111-0077}{ORCID}) 30 | } 31 | 32 | Other contributors: 33 | \itemize{ 34 | \item Posit Software, PBC (03wc8by49) [copyright holder, funder] 35 | } 36 | 37 | } 38 | \keyword{internal} 39 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: experimental 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | experimental 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/man/figures/logo.png -------------------------------------------------------------------------------- /man/google_upload.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/provider-gemini-upload.R 3 | \name{google_upload} 4 | \alias{google_upload} 5 | \title{Upload a file to gemini} 6 | \usage{ 7 | google_upload( 8 | path, 9 | base_url = "https://generativelanguage.googleapis.com/v1beta/", 10 | api_key = NULL, 11 | mime_type = NULL 12 | ) 13 | } 14 | \arguments{ 15 | \item{path}{Path to a file to upload.} 16 | 17 | \item{base_url}{The base URL to the endpoint; the default uses OpenAI.} 18 | 19 | \item{api_key}{API key to use for authentication. 20 | 21 | You generally should not supply this directly, but instead set the \code{GOOGLE_API_KEY} environment variable. 22 | The best place to set this is in \code{.Renviron}, 23 | which you can easily edit by calling \code{usethis::edit_r_environ()}. 24 | For Gemini, you can alternatively set \code{GEMINI_API_KEY}.} 25 | 26 | \item{mime_type}{Optionally, specify the mime type of the file. 27 | If not specified, will be guesses from the file extension.} 28 | } 29 | \value{ 30 | A \verb{} object that can be passed to \verb{$chat()}. 31 | } 32 | \description{ 33 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 34 | 35 | This function uploads a file then waits for Gemini to finish processing it 36 | so that you can immediately use it in a prompt. It's experimental because 37 | it's currently Gemini specific, and we expect other providers to evolve 38 | similar feature in the future. 39 | 40 | Uploaded files are automatically deleted after 2 days. Each file must be 41 | less than 2 GB and you can upload a total of 20 GB. ellmer doesn't currently 42 | provide a way to delete files early; please 43 | \href{https://github.com/tidyverse/ellmer/issues}{file an issue} if this would 44 | be useful for you. 45 | } 46 | \examples{ 47 | \dontrun{ 48 | file <- google_upload("path/to/file.pdf") 49 | 50 | chat <- chat_google_gemini() 51 | chat$chat(file, "Give me a three paragraph summary of this PDF") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /man/has_credentials.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{has_credentials} 4 | \alias{has_credentials} 5 | \title{Are credentials avaiable?} 6 | \usage{ 7 | has_credentials(provider) 8 | } 9 | \arguments{ 10 | \item{provider}{Provider name.} 11 | } 12 | \description{ 13 | Used for examples/testing. 14 | } 15 | \keyword{internal} 16 | -------------------------------------------------------------------------------- /man/interpolate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interpolate.R 3 | \name{interpolate} 4 | \alias{interpolate} 5 | \alias{interpolate_file} 6 | \alias{interpolate_package} 7 | \title{Helpers for interpolating data into prompts} 8 | \usage{ 9 | interpolate(prompt, ..., .envir = parent.frame()) 10 | 11 | interpolate_file(path, ..., .envir = parent.frame()) 12 | 13 | interpolate_package(package, path, ..., .envir = parent.frame()) 14 | } 15 | \arguments{ 16 | \item{prompt}{A prompt string. You should not generally expose this 17 | to the end user, since glue interpolation makes it easy to run arbitrary 18 | code.} 19 | 20 | \item{...}{Define additional temporary variables for substitution.} 21 | 22 | \item{.envir}{Environment to evaluate \code{...} expressions in. Used when 23 | wrapping in another function. See \code{vignette("wrappers", package = "glue")} 24 | for more details.} 25 | 26 | \item{path}{A path to a prompt file (often a \code{.md}).} 27 | 28 | \item{package}{Package name.} 29 | } 30 | \value{ 31 | A \{glue\} string. 32 | } 33 | \description{ 34 | These functions are lightweight wrappers around 35 | \href{https://glue.tidyverse.org/}{glue} that make it easier to interpolate 36 | dynamic data into a static prompt: 37 | \itemize{ 38 | \item \code{interpolate()} works with a string. 39 | \item \code{interpolate_file()} works with a file. 40 | \item \code{interpolate_package()} works with a file in the \code{insts/prompt} 41 | directory of a package. 42 | } 43 | 44 | Compared to glue, dynamic values should be wrapped in \code{{{ }}}, making it 45 | easier to include R code and JSON in your prompt. 46 | } 47 | \examples{ 48 | joke <- "You're a cool dude who loves to make jokes. Tell me a joke about {{topic}}." 49 | 50 | # You can supply valuese directly: 51 | interpolate(joke, topic = "bananas") 52 | 53 | # Or allow interpolate to find them in the current environment: 54 | topic <- "applies" 55 | interpolate(joke) 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /man/live_console.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shiny.R 3 | \name{live_console} 4 | \alias{live_console} 5 | \alias{live_browser} 6 | \title{Open a live chat application} 7 | \usage{ 8 | live_console(chat, quiet = FALSE) 9 | 10 | live_browser(chat, quiet = FALSE) 11 | } 12 | \arguments{ 13 | \item{chat}{A chat object created by \code{\link[=chat_openai]{chat_openai()}} or friends.} 14 | 15 | \item{quiet}{If \code{TRUE}, suppresses the initial message that explains how 16 | to use the console.} 17 | } 18 | \value{ 19 | (Invisibly) The input \code{chat}. 20 | } 21 | \description{ 22 | \itemize{ 23 | \item \code{live_console()} lets you chat interactively in the console. 24 | \item \code{live_browser()} lets you chat interactively in a browser. 25 | } 26 | 27 | Note that these functions will mutate the input \code{chat} object as 28 | you chat because your turns will be appended to the history. 29 | } 30 | \examples{ 31 | \dontrun{ 32 | chat <- chat_anthropic() 33 | live_console(chat) 34 | live_browser(chat) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /man/params.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/params.R 3 | \name{params} 4 | \alias{params} 5 | \title{Standard model parameters} 6 | \usage{ 7 | params( 8 | temperature = NULL, 9 | top_p = NULL, 10 | top_k = NULL, 11 | frequency_penalty = NULL, 12 | presence_penalty = NULL, 13 | seed = NULL, 14 | max_tokens = NULL, 15 | log_probs = NULL, 16 | stop_sequences = NULL, 17 | ... 18 | ) 19 | } 20 | \arguments{ 21 | \item{temperature}{Temperature of the sampling distribution.} 22 | 23 | \item{top_p}{The cumulative probability for token selection.} 24 | 25 | \item{top_k}{The number of highest probability vocabulary tokens to keep.} 26 | 27 | \item{frequency_penalty}{Frequency penalty for generated tokens.} 28 | 29 | \item{presence_penalty}{Presence penalty for generated tokens.} 30 | 31 | \item{seed}{Seed for random number generator.} 32 | 33 | \item{max_tokens}{Maximum number of tokens to generate.} 34 | 35 | \item{log_probs}{Include the log probabilities in the output?} 36 | 37 | \item{stop_sequences}{A character vector of tokens to stop generation on.} 38 | 39 | \item{...}{Additional named parameters to send to the provider.} 40 | } 41 | \description{ 42 | This helper function makes it easier to create a list of parameters used 43 | across many models. The parameter names are automatically standardised and 44 | included in the correctly place in the API call. 45 | 46 | Note that parameters that are not supported by a given provider will generate 47 | a warning, not an error. This allows you to use the same set of parameters 48 | across multiple providers. 49 | } 50 | -------------------------------------------------------------------------------- /man/token_usage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tokens.R 3 | \name{token_usage} 4 | \alias{token_usage} 5 | \title{Report on token usage in the current session} 6 | \usage{ 7 | token_usage() 8 | } 9 | \value{ 10 | A data frame 11 | } 12 | \description{ 13 | Call this function to find out the cumulative number of tokens that you 14 | have sent and recieved in the current session. The price will be shown 15 | if known. 16 | } 17 | \examples{ 18 | token_usage() 19 | } 20 | -------------------------------------------------------------------------------- /man/tool.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tools-def.R 3 | \name{tool} 4 | \alias{tool} 5 | \title{Define a tool} 6 | \usage{ 7 | tool( 8 | .fun, 9 | .description, 10 | ..., 11 | .name = NULL, 12 | .convert = TRUE, 13 | .annotations = list() 14 | ) 15 | } 16 | \arguments{ 17 | \item{.fun}{The function to be invoked when the tool is called. The return 18 | value of the function is sent back to the chatbot. 19 | 20 | Expert users can customize the tool result by returning a 21 | \link{ContentToolResult} object.} 22 | 23 | \item{.description}{A detailed description of what the function does. 24 | Generally, the more information that you can provide here, the better.} 25 | 26 | \item{...}{Name-type pairs that define the arguments accepted by the 27 | function. Each element should be created by a \code{\link[=type_boolean]{type_*()}} 28 | function.} 29 | 30 | \item{.name}{The name of the function.} 31 | 32 | \item{.convert}{Should JSON inputs be automatically convert to their 33 | R data type equivalents? Defaults to \code{TRUE}.} 34 | 35 | \item{.annotations}{Additional properties that describe the tool and its 36 | behavior. Usually created by \code{\link[=tool_annotations]{tool_annotations()}}, where you can find a 37 | description of the annotation properties recommended by the \href{https://modelcontextprotocol.io/introduction}{Model Context Protocol}.} 38 | } 39 | \value{ 40 | An S7 \code{ToolDef} object. 41 | } 42 | \description{ 43 | Define an R function for use by a chatbot. The function will always be 44 | run in the current R instance. 45 | 46 | Learn more in \code{vignette("tool-calling")}. 47 | } 48 | \examples{ 49 | \dontshow{if (has_credentials("openai")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 50 | 51 | # First define the metadata that the model uses to figure out when to 52 | # call the tool 53 | tool_rnorm <- tool( 54 | rnorm, 55 | "Drawn numbers from a random normal distribution", 56 | n = type_integer("The number of observations. Must be a positive integer."), 57 | mean = type_number("The mean value of the distribution."), 58 | sd = type_number("The standard deviation of the distribution. Must be a non-negative number."), 59 | .annotations = tool_annotations( 60 | title = "Draw Random Normal Numbers", 61 | read_only_hint = TRUE, 62 | open_world_hint = FALSE 63 | ) 64 | ) 65 | chat <- chat_openai() 66 | # Then register it 67 | chat$register_tool(tool_rnorm) 68 | 69 | # Then ask a question that needs it. 70 | chat$chat(" 71 | Give me five numbers from a random normal distribution. 72 | ") 73 | 74 | # Look at the chat history to see how tool calling works: 75 | # Assistant sends a tool request which is evaluated locally and 76 | # results are send back in a tool result. 77 | \dontshow{\}) # examplesIf} 78 | } 79 | \seealso{ 80 | Other tool calling helpers: 81 | \code{\link{tool_annotations}()}, 82 | \code{\link{tool_reject}()} 83 | } 84 | \concept{tool calling helpers} 85 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /pkgdown/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/web-app-manifest-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "maskable" 10 | }, 11 | { 12 | "src": "/web-app-manifest-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | } 17 | ], 18 | "theme_color": "#ffffff", 19 | "background_color": "#ffffff", 20 | "display": "standalone" 21 | } -------------------------------------------------------------------------------- /pkgdown/favicon/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /pkgdown/favicon/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/pkgdown/favicon/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | cloud.noindex 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | ## New problems (1) 4 | 5 | |package |version |error |warning |note | 6 | |:-------|:-------|:------|:-------|:----| 7 | |[GitAI](problems.md#gitai)|0.1.0 |__+1__ | | | 8 | 9 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 9 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 1 new problems 6 | * We failed to check 0 packages 7 | 8 | Issues with CRAN packages are summarised below. 9 | 10 | ### New problems 11 | (This reports the first line of each new failure) 12 | 13 | * GitAI 14 | checking tests ... ERROR 15 | 16 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | # GitAI 2 | 3 |
4 | 5 | * Version: 0.1.0 6 | * GitHub: NA 7 | * Source code: https://github.com/cran/GitAI 8 | * Date/Publication: 2025-02-20 18:40:16 UTC 9 | * Number of recursive dependencies: 74 10 | 11 | Run `revdepcheck::cloud_details(, "GitAI")` for more info 12 | 13 |
14 | 15 | ## Newly broken 16 | 17 | * checking tests ... ERROR 18 | ``` 19 | Running ‘testthat.R’ 20 | Running the tests in ‘tests/testthat.R’ failed. 21 | Complete output: 22 | > # This file is part of the standard setup for testthat. 23 | > # It is recommended that you do not modify it. 24 | > # 25 | > # Where should you do additional test configuration? 26 | > # Learn more about the roles of various files in: 27 | > # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 28 | > # * https://testthat.r-lib.org/articles/special-files.html 29 | ... 30 | 4. └─GitAI (local) ``(model = "gpt-4o-mini", seed = NULL, echo = "none") 31 | 5. └─GitAI:::mock_chat_method(...) at tests/testthat/setup.R:46:3 32 | 6. ├─rlang::exec(provider_class, !!!provider_args) at tests/testthat/setup.R:24:3 33 | 7. └─ellmer (local) ``(...) 34 | 8. ├─S7::new_object(...) 35 | 9. └─ellmer::Provider(...) 36 | 37 | [ FAIL 4 | WARN 5 | SKIP 7 | PASS 35 ] 38 | Error: Test failures 39 | Execution halted 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 7 | # * https://testthat.r-lib.org/articles/special-files.html 8 | 9 | library(testthat) 10 | library(ellmer) 11 | 12 | test_check("ellmer") 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/batch-chat.md: -------------------------------------------------------------------------------- 1 | # errors if chat/provider/prompts don't match previous run 2 | 3 | Code 4 | batch_chat(chat, prompts, path) 5 | Condition 6 | Error in `batch_chat()`: 7 | ! provider, prompts, and user_turns don't match stored values. 8 | i Do you need to pick a different `path`? 9 | 10 | # informative error for bad inputs 11 | 12 | Code 13 | batch_chat("x") 14 | Condition 15 | Error in `batch_chat()`: 16 | ! `chat` must be a object. 17 | Code 18 | batch_chat(chat_ollama) 19 | Condition 20 | Error in `batch_chat()`: 21 | ! Batch requests are not currently supported by this provider. 22 | Code 23 | batch_chat(chat_openai, "a") 24 | Condition 25 | Error in `batch_chat()`: 26 | ! `prompts` must be a list or prompt, not the string "a". 27 | Code 28 | batch_chat(chat_openai, list("a"), path = 1) 29 | Condition 30 | Error in `batch_chat()`: 31 | ! `path` must be a single string, not the number 1. 32 | Code 33 | batch_chat(chat_openai, list("a"), path = "x", wait = 1) 34 | Condition 35 | Error in `batch_chat()`: 36 | ! `wait` must be `TRUE` or `FALSE`, not the number 1. 37 | 38 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/chat-structured.md: -------------------------------------------------------------------------------- 1 | # useful error if no ContentJson 2 | 3 | Code 4 | extract_data(turn) 5 | Condition 6 | Error in `extract_data()`: 7 | ! Data extraction failed: 0 data results recieved. 8 | 9 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/content-image.md: -------------------------------------------------------------------------------- 1 | # errors with invalid data urls 2 | 3 | Code 4 | content_image_url("data:base64,abcd") 5 | Condition 6 | Error in `content_image_url()`: 7 | ! `url` is not a valid data url. 8 | 9 | # image resizing 10 | 11 | Code 12 | content_image_file("DOESNTEXIST") 13 | Condition 14 | Error in `content_image_file()`: 15 | ! DOESNTEXIST must be an existing file. 16 | Code 17 | content_image_file(test_path("test-content.R")) 18 | Condition 19 | Error in `content_image_file()`: 20 | ! Unsupported image file extension: r. 21 | Code 22 | content_image_file(img_file, resize = TRUE) 23 | Condition 24 | Error in `content_image_file()`: 25 | ! `resize` must be a single string, not `TRUE`. 26 | Code 27 | content_image_file(img_file, resize = "blah") 28 | Condition 29 | Error: 30 | ! Invalid geometry string: blah 31 | 32 | # useful errors if no display 33 | 34 | Code 35 | content_image_plot() 36 | Condition 37 | Error in `content_image_plot()`: 38 | ! Can't record plot because display list is inhibited. 39 | i Turn it on with `dev.control('enable')`. 40 | 41 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/content-tools.md: -------------------------------------------------------------------------------- 1 | # invoke_tools() echoes tool requests and results 2 | 3 | Code 4 | . <- coro::collect(invoke_tools(turn, echo = "output")) 5 | Message 6 | ( ) [tool call] my_tool() 7 | o #> 1 8 | ( ) [tool call] my_tool(x = 1) 9 | # #> Error: Unused argument: x 10 | ( ) [tool call] tool_list() 11 | o #> {"a":1,"b":2} 12 | ( ) [tool call] tool_chr() 13 | o #> a 14 | #> b 15 | #> c 16 | ( ) [tool call] tool_abort() 17 | # #> Error: Unexpected input 18 | #> i Please revise and try again. 19 | 20 | # invoke_tools_async() echoes tool requests and results 21 | 22 | Code 23 | . <- sync(gen_async_promise_all(invoke_tools_async(turn, echo = "output"))) 24 | Message 25 | ( ) [tool call] my_tool() 26 | ( ) [tool call] my_tool(x = 1) 27 | ( ) [tool call] tool_list() 28 | ( ) [tool call] tool_chr() 29 | ( ) [tool call] tool_abort() 30 | # #> Error: Unused argument: x 31 | # #> Error: Unexpected input 32 | #> i Please revise and try again. 33 | o #> 1 34 | o #> {"a":1,"b":2} 35 | o #> a 36 | #> b 37 | #> c 38 | Code 39 | . <- sync(coro::async_collect(invoke_tools_async(turn, echo = "output"))) 40 | Message 41 | ( ) [tool call] my_tool() 42 | o #> 1 43 | ( ) [tool call] my_tool(x = 1) 44 | # #> Error: Unused argument: x 45 | ( ) [tool call] tool_list() 46 | o #> {"a":1,"b":2} 47 | ( ) [tool call] tool_chr() 48 | o #> a 49 | #> b 50 | #> c 51 | ( ) [tool call] tool_abort() 52 | # #> Error: Unexpected input 53 | #> i Please revise and try again. 54 | 55 | # tool error warnings 56 | 57 | Code 58 | warn_tool_errors(errors) 59 | Condition 60 | Warning: 61 | Failed to evaluate 2 tool calls. 62 | x [returns_json (call1)]: The JSON was invalid: {[1, 2, 3]} 63 | x [throws (call2)]: went boom! 64 | 65 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/interpolate.md: -------------------------------------------------------------------------------- 1 | # checks inputs 2 | 3 | Code 4 | interpolate(1) 5 | Condition 6 | Error in `interpolate()`: 7 | ! `prompt` must be a single string, not the number 1. 8 | Code 9 | interpolate("x", 1) 10 | Condition 11 | Error in `interpolate()`: 12 | ! All elements of `...` must be named 13 | 14 | # has a nice print method 15 | 16 | Code 17 | interpolate("Hi!") 18 | Output 19 | [1] | Hi! 20 | 21 | # print method truncates many elements 22 | 23 | Code 24 | print(prompt, max_items = 1) 25 | Output 26 | [1] | x 27 | | y 28 | ... and 1 more. 29 | Code 30 | print(prompt, max_lines = 2) 31 | Output 32 | [1] | x 33 | | y 34 | | ... 35 | ... and 1 more. 36 | Code 37 | print(prompt, max_lines = 3) 38 | Output 39 | [1] | x 40 | | y 41 | [2] | a 42 | | ... 43 | 44 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/params.md: -------------------------------------------------------------------------------- 1 | # checks its inputs 2 | 3 | Code 4 | params(temperature = "x") 5 | Condition 6 | Error in `params()`: 7 | ! `temperature` must be a number or `NULL`, not the string "x". 8 | Code 9 | params(top_p = "x") 10 | Condition 11 | Error in `params()`: 12 | ! `top_p` must be a number or `NULL`, not the string "x". 13 | Code 14 | params(top_k = "x") 15 | Condition 16 | Error in `params()`: 17 | ! `top_k` must be a whole number or `NULL`, not the string "x". 18 | Code 19 | params(frequency_penalty = "x") 20 | Condition 21 | Error in `params()`: 22 | ! `frequency_penalty` must be a number or `NULL`, not the string "x". 23 | Code 24 | params(presence_penalty = "x") 25 | Condition 26 | Error in `params()`: 27 | ! `presence_penalty` must be a number or `NULL`, not the string "x". 28 | Code 29 | params(seed = "x") 30 | Condition 31 | Error in `params()`: 32 | ! `seed` must be a whole number or `NULL`, not the string "x". 33 | Code 34 | params(max_tokens = "x") 35 | Condition 36 | Error in `params()`: 37 | ! `max_tokens` must be a whole number or `NULL`, not the string "x". 38 | Code 39 | params(log_probs = 1) 40 | Condition 41 | Error in `params()`: 42 | ! `log_probs` must be `TRUE`, `FALSE`, or `NULL`, not the number 1. 43 | Code 44 | params(stop_sequences = 1) 45 | Condition 46 | Error in `params()`: 47 | ! `stop_sequences` must be a character vector or `NULL`, not the number 1. 48 | 49 | # standardise_params warns about unknown args 50 | 51 | Code 52 | . <- standardise_params(test_params, provider_params) 53 | Condition 54 | Warning: 55 | Ignoring unsupported parameters: "top_p" 56 | 57 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-azure.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_azure_openai_test() 5 | Message 6 | Using api_version = "2024-10-21". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | # Azure request headers are generated correctly 18 | 19 | Code 20 | str(req$headers) 21 | Output 22 | 23 | $ api-key: chr "key" 24 | 25 | --- 26 | 27 | Code 28 | str(req$headers) 29 | Output 30 | 31 | $ Authorization: chr "Bearer token" 32 | 33 | --- 34 | 35 | Code 36 | str(req$headers) 37 | Output 38 | 39 | $ api-key : chr "key" 40 | $ Authorization: chr "Bearer token" 41 | 42 | # service principal authentication requests look correct 43 | 44 | Code 45 | list(url = req$url, headers = req$headers, body = req$body$data) 46 | Output 47 | $url 48 | [1] "https://login.microsoftonline.com/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/oauth2/v2.0/token" 49 | 50 | $headers 51 | 52 | Accept: application/json 53 | 54 | $body 55 | $body$grant_type 56 | [1] "client_credentials" 57 | 58 | $body$scope 59 | [1] "https%3A%2F%2Fcognitiveservices.azure.com%2F.default" 60 | 61 | $body$client_id 62 | [1] "id" 63 | 64 | $body$client_secret 65 | [1] "secret" 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-bedrock.md: -------------------------------------------------------------------------------- 1 | # handles errors 2 | 3 | Code 4 | chat$chat("What is 1 + 1?", echo = FALSE) 5 | Condition 6 | Error in `req_perform()`: 7 | ! HTTP 400 Bad Request. 8 | * STRING_VALUE cannot be converted to Float 9 | Code 10 | chat$chat("What is 1 + 1?", echo = TRUE) 11 | Condition 12 | Error in `req_perform_connection()`: 13 | ! HTTP 400 Bad Request. 14 | * STRING_VALUE cannot be converted to Float 15 | 16 | # defaults are reported 17 | 18 | Code 19 | . <- chat_aws_bedrock() 20 | Message 21 | Using model = "anthropic.claude-3-5-sonnet-20240620-v1:0". 22 | 23 | # all tool variations work 24 | 25 | Code 26 | chat$chat("Great. Do it again.") 27 | Condition 28 | Error: 29 | ! Can't use async tools with `$chat()` or `$stream()`. 30 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 31 | 32 | # can use images 33 | 34 | Code 35 | . <- chat$chat("What's in this image?", image_remote) 36 | Condition 37 | Error in `method(as_json, list(ellmer::ProviderAWSBedrock, ellmer::ContentImageRemote))`: 38 | ! Bedrock doesn't support remote images 39 | 40 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-claude.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_anthropic() 5 | Message 6 | Using model = "claude-sonnet-4-20250514". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | # max_tokens is deprecated 18 | 19 | Code 20 | chat <- chat_anthropic_test(max_tokens = 10) 21 | Condition 22 | Warning: 23 | The `max_tokens` argument of `chat_anthropic()` is deprecated as of ellmer 0.2.0. 24 | i Please use the `params` argument instead. 25 | 26 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-cloudflare.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_cloudflare() 5 | Message 6 | Using model = "@cf/meta/llama-3.3-70b-instruct-fp8-fast". 7 | 8 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-cortex.md: -------------------------------------------------------------------------------- 1 | # Cortex turn formatting 2 | 3 | Code 4 | cat(turn@text) 5 | Output 6 | This semantic data model... 7 | 8 | ```sql 9 | SELECT SUM(revenue) FROM key_business_metrics 10 | ``` 11 | 12 | #### Suggestions 13 | 14 | - What is the total quantity sold for each product last quarter? 15 | - What is the average discount percentage for orders from the United States? 16 | - What is the average price of products in the 'electronics' category? 17 | 18 | --- 19 | 20 | Code 21 | cat(format(turn)) 22 | Output 23 | This semantic data model... 24 | SQL: `SELECT SUM(revenue) FROM key_business_metrics` 25 | Suggestions: 26 | * What is the total quantity sold for each product last quarter? 27 | * What is the average discount percentage for orders from the United States? 28 | * What is the average price of products in the 'electronics' category? 29 | 30 | # Cortex API requests are generated correctly 31 | 32 | Code 33 | req$url 34 | Output 35 | [1] "https://testorg-test_account.snowflakecomputing.com/api/v2/cortex/analyst/message" 36 | 37 | --- 38 | 39 | Code 40 | req$headers 41 | Output 42 | 43 | Authorization: 44 | X-Snowflake-Authorization-Token-Type: 45 | 46 | --- 47 | 48 | Code 49 | print_json(req$body$data) 50 | Output 51 | { 52 | "messages": [ 53 | { 54 | "role": "user", 55 | "content": [ 56 | { 57 | "type": "text", 58 | "text": "Tell me about my data." 59 | } 60 | ] 61 | } 62 | ], 63 | "stream": false, 64 | "semantic_model_file": "@my_db.my_schema.my_stage/model.yaml" 65 | } 66 | 67 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-databricks.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_databricks() 5 | Message 6 | Using model = "databricks-claude-3-7-sonnet". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | # M2M authentication requests look correct 18 | 19 | Code 20 | list(url = req$url, headers = req$headers, body = req$body$data) 21 | Output 22 | $url 23 | [1] "https://example.cloud.databricks.com/oidc/v1/token" 24 | 25 | $headers 26 | 27 | Authorization: 28 | Accept: application/json 29 | 30 | $body 31 | $body$grant_type 32 | [1] "client_credentials" 33 | 34 | $body$scope 35 | [1] "all-apis" 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-deepseek.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_deepseek() 5 | Message 6 | Using model = "deepseek-chat". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error in `FUN()`: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-gemini.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_google_gemini() 5 | Message 6 | Using model = "gemini-2.0-flash". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | # can use images 18 | 19 | Code 20 | . <- chat$chat("What's in this image?", image_remote) 21 | Condition 22 | Error in `method(as_json, list(ellmer::ProviderGoogleGemini, ellmer::ContentImageRemote))`: 23 | ! Gemini doesn't support remote images 24 | 25 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-groq.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_groq() 5 | Message 6 | Using model = "llama3-8b-8192". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error in `FUN()`: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-huggingface.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_huggingface_test() 5 | 6 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-mistral.md: -------------------------------------------------------------------------------- 1 | # can handle errors 2 | 3 | Code 4 | chat$chat("Hi") 5 | Condition 6 | Error in `req_perform()`: 7 | ! HTTP 400 Bad Request. 8 | * Invalid model: doesnt-exist 9 | 10 | # defaults are reported 11 | 12 | Code 13 | . <- chat_mistral() 14 | Message 15 | Using model = "mistral-large-latest". 16 | 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-ollama.md: -------------------------------------------------------------------------------- 1 | # includes list of models in error message if `model` is missing 2 | 3 | Code 4 | chat_ollama() 5 | Condition 6 | Error in `chat_ollama()`: 7 | ! Must specify `model`. 8 | i Locally installed models: "llama3". 9 | 10 | # checks that requested model is installed 11 | 12 | Code 13 | chat_ollama(model = "not-a-real-model") 14 | Condition 15 | Error in `chat_ollama()`: 16 | ! Model "not-a-real-model" is not installed locally. 17 | i Run `ollama pull not-a-real-model` in your terminal or `ollamar::pull("not-a-real-model")` in R to install the model. 18 | i See locally installed models with `ellmer::models_ollama()`. 19 | 20 | # as_json specialised for Ollama 21 | 22 | Code 23 | as_json(stub, type_object(.additional_properties = TRUE)) 24 | Condition 25 | Error in `method(as_json, list(ellmer::ProviderOllama, ellmer::TypeObject))`: 26 | ! `.additional_properties` not supported for Ollama. 27 | 28 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-openai.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_openai() 5 | Message 6 | Using model = "gpt-4.1". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | # as_json specialised for OpenAI 18 | 19 | Code 20 | as_json(stub, type_object(.additional_properties = TRUE)) 21 | Condition 22 | Error in `method(as_json, list(ellmer::ProviderOpenAI, ellmer::TypeObject))`: 23 | ! `.additional_properties` not supported for OpenAI. 24 | 25 | # seed is deprecated, but still honored 26 | 27 | Code 28 | chat <- chat_openai_test(seed = 1) 29 | Condition 30 | Warning: 31 | The `seed` argument of `chat_openai()` is deprecated as of ellmer 0.2.0. 32 | i Please use the `params` argument instead. 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-openrouter.md: -------------------------------------------------------------------------------- 1 | # handles errors 2 | 3 | Code 4 | chat$chat("What is 1 + 1?", echo = FALSE) 5 | Condition 6 | Error in `method(value_turn, ellmer::ProviderOpenRouter)`: 7 | ! message 8 | Code 9 | chat$chat("What is 1 + 1?", echo = TRUE) 10 | Condition 11 | Error in `method(stream_parse, ellmer::ProviderOpenRouter)`: 12 | ! message 13 | 14 | # all tool variations work 15 | 16 | Code 17 | chat$chat("Great. Do it again.") 18 | Condition 19 | Error in `FUN()`: 20 | ! Can't use async tools with `$chat()` or `$stream()`. 21 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 22 | 23 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-portkey.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_portkey() 5 | 6 | # all tool variations work 7 | 8 | Code 9 | chat$chat("Great. Do it again.") 10 | Condition 11 | Error in `FUN()`: 12 | ! Can't use async tools with `$chat()` or `$stream()`. 13 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 14 | 15 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/provider-snowflake.md: -------------------------------------------------------------------------------- 1 | # defaults are reported 2 | 3 | Code 4 | . <- chat_snowflake() 5 | Message 6 | Using model = "claude-3-7-sonnet". 7 | 8 | # all tool variations work 9 | 10 | Code 11 | chat$chat("Great. Do it again.") 12 | Condition 13 | Error: 14 | ! Can't use async tools with `$chat()` or `$stream()`. 15 | i Async tools are supported, but you must use `$chat_async()` or `$stream_async()`. 16 | 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/tokens.md: -------------------------------------------------------------------------------- 1 | # useful message if no tokens 2 | 3 | Code 4 | token_usage() 5 | Message 6 | x No recorded usage in this session 7 | 8 | # can retrieve and log tokens 9 | 10 | Code 11 | token_usage() 12 | Output 13 | provider model input output price 14 | 1 testprovider test 10 60 NA 15 | 16 | # token_usage() shows price if available 17 | 18 | Code 19 | token_usage() 20 | Output 21 | provider model input output price 22 | 1 testprovider test 12300000 678000 $1.24 23 | 24 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/tools-def-auto.md: -------------------------------------------------------------------------------- 1 | # roxygen2 comment extraction works 2 | 3 | Code 4 | extract_comments_and_signature(has_roxygen_comments) 5 | Output 6 | [1] "#' A function for foo-ing three numbers.\n#'\n#' @param x The first param\n#' @param y The second param\n#' @param z Take a guess\n#' @returns The result of x %foo% y %foo% z.\nfunction (x, y, z = pi - 3.14) ..." 7 | 8 | --- 9 | 10 | Code 11 | extract_comments_and_signature(aliased_function) 12 | Output 13 | [1] "#' A function for foo-ing three numbers.\n#'\n#' @param x The first param\n#' @param y The second param\n#' @param z Take a guess\n#' @returns The result of x %foo% y %foo% z.\nfunction (x, y, z = pi - 3.14) ..." 14 | 15 | --- 16 | 17 | Code 18 | extract_comments_and_signature(indented_comments) 19 | Output 20 | [1] " #' A function for foo-ing three numbers.\n #'\n #' @param x The first param\n #' @param y The second param\n #' @param z Take a guess\n #' @returns The result of x %foo% y %foo% z.\nfunction (x, y, z = pi - 3.14) ..." 21 | 22 | --- 23 | 24 | Code 25 | extract_comments_and_signature(no_srcfile) 26 | Output 27 | [1] " #' A function for foo-ing three numbers.\nfunction (a, b, c = pi - 3.14) ..." 28 | 29 | # basic signature extraction works 30 | 31 | Code 32 | extract_comments_and_signature(no_roxygen_comments) 33 | Output 34 | [1] "function (i, j, k = pi - 3.14) ..." 35 | 36 | # checks its inputs 37 | 38 | Code 39 | create_tool_def(print, model = "gpt-4", chat = chat_google_gemini()) 40 | Condition 41 | Error in `create_tool_def()`: 42 | ! Exactly one of `model` or `chat` must be supplied. 43 | Code 44 | create_tool_def(print, chat = 1) 45 | Condition 46 | Error in `create_tool_def()`: 47 | ! `chat` must be a object or `NULL`, not the number 1. 48 | 49 | # model is deprecated 50 | 51 | Code 52 | . <- create_tool_def(print, model = "gpt-4", echo = FALSE) 53 | Condition 54 | Warning: 55 | The `model` argument of `create_tool_def()` is deprecated as of ellmer 1.0.0. 56 | i Please use the `chat` argument instead. 57 | 58 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/tools-def.md: -------------------------------------------------------------------------------- 1 | # tool_annotations(): checks its inputs 2 | 3 | Code 4 | tool_annotations(title = list("Something unexpected")) 5 | Condition 6 | Error in `tool_annotations()`: 7 | ! `title` must be a character vector or `NULL`, not a list. 8 | Code 9 | tool_annotations(read_only_hint = "yes") 10 | Condition 11 | Error in `tool_annotations()`: 12 | ! `read_only_hint` must be `TRUE`, `FALSE`, or `NULL`, not the string "yes". 13 | Code 14 | tool_annotations(open_world_hint = "yes") 15 | Condition 16 | Error in `tool_annotations()`: 17 | ! `open_world_hint` must be `TRUE`, `FALSE`, or `NULL`, not the string "yes". 18 | Code 19 | tool_annotations(idempotent_hint = "no") 20 | Condition 21 | Error in `tool_annotations()`: 22 | ! `idempotent_hint` must be `TRUE`, `FALSE`, or `NULL`, not the string "no". 23 | Code 24 | tool_annotations(destructive_hint = "no") 25 | Condition 26 | Error in `tool_annotations()`: 27 | ! `destructive_hint` must be `TRUE`, `FALSE`, or `NULL`, not the string "no". 28 | 29 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/turns.md: -------------------------------------------------------------------------------- 1 | # normalize_turns throws useful errors 2 | 3 | Code 4 | normalize_turns(1) 5 | Condition 6 | Error: 7 | ! `turns` must be an unnamed list or `NULL`, not the number 1. 8 | Code 9 | normalize_turns(list(1)) 10 | Condition 11 | Error in `normalize_turns()`: 12 | ! Every element of `turns` must be a `turn`. 13 | Code 14 | normalize_turns(list(sys_msg, user_msg), 1) 15 | Condition 16 | Error in `normalize_turns()`: 17 | ! `system_prompt` must be a single string or `NULL`, not the number 1. 18 | Code 19 | normalize_turns(list(sys_msg, user_msg), "foo2") 20 | Condition 21 | Error: 22 | ! `system_prompt` and `turns[[1]]` can't contain conflicting system prompts. 23 | 24 | # as_user_turn gives useful errors 25 | 26 | Code 27 | as_user_turn(list()) 28 | Condition 29 | Error: 30 | ! `...` must contain at least one input. 31 | Code 32 | as_user_turn(list(x = 1)) 33 | Condition 34 | Error: 35 | ! `...` must be unnamed. 36 | Code 37 | as_user_turn(1) 38 | Condition 39 | Error in `FUN()`: 40 | ! `...` must be made up strings or objects, not the number 1. 41 | 42 | # as_user_turns gives useful errors 43 | 44 | Code 45 | as_user_turns(1) 46 | Condition 47 | Error: 48 | ! `1` must be a list or prompt, not the number 1. 49 | Code 50 | as_user_turns(x) 51 | Condition 52 | Error in `FUN()`: 53 | ! `x[[1]]` must be made up strings or objects, not the number 1. 54 | 55 | # turns have a reasonable print method 56 | 57 | Code 58 | Turn("user", "hello") 59 | Output 60 | 61 | hello 62 | 63 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utils-S7.md: -------------------------------------------------------------------------------- 1 | # prop_whole_number validates inputs 2 | 3 | Code 4 | check_prop()("x") 5 | Condition 6 | Error: 7 | ! object properties are invalid: 8 | - @prop must be , not 9 | Code 10 | check_prop()(c(1:2)) 11 | Condition 12 | Error: 13 | ! object properties are invalid: 14 | - @prop must be , not 15 | Code 16 | check_prop()(1.5) 17 | Condition 18 | Error: 19 | ! object properties are invalid: 20 | - @prop must be a whole number, not the number 1.5. 21 | Code 22 | check_prop(min = 1)(0) 23 | Condition 24 | Error: 25 | ! object properties are invalid: 26 | - @prop must be at least 1, not 0. 27 | Code 28 | check_prop(max = -1)(0) 29 | Condition 30 | Error: 31 | ! object properties are invalid: 32 | - @prop must be at most -1, not 0. 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utils-callbacks.md: -------------------------------------------------------------------------------- 1 | # CallbackManager catches argument mismatches 2 | 3 | Code 4 | callbacks$add("foo") 5 | Condition 6 | Error: 7 | ! `callback` must be a function, not the string "foo". 8 | Code 9 | callbacks$add(function(foo) NULL) 10 | Condition 11 | Error: 12 | ! `callback` must have the argument `data`; it currently has `foo`. 13 | Code 14 | callbacks$add(function(x, y) x + y) 15 | Condition 16 | Error: 17 | ! `callback` must have the argument `data`; it currently has `x` and `y`. 18 | 19 | --- 20 | 21 | Code 22 | callbacks$invoke() 23 | Condition 24 | Error in `private$callbacks[[as.character(id)]]()`: 25 | ! argument "data" is missing, with no default 26 | Code 27 | callbacks$invoke(1, 2) 28 | Condition 29 | Error in `private$callbacks[[as.character(id)]]()`: 30 | ! unused argument (2) 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utils.md: -------------------------------------------------------------------------------- 1 | # informative error if no key 2 | 3 | Code 4 | key_get("FOO") 5 | Condition 6 | Error: 7 | ! Can't find env var `FOO`. 8 | 9 | # echo="output" replaces echo="text" 10 | 11 | Code 12 | expect_equal(check_echo("text"), "output") 13 | Condition 14 | Warning: 15 | `echo = "text"` was deprecated in ellmer 0.2.0. 16 | i Please use `echo = "output"` instead. 17 | 18 | -------------------------------------------------------------------------------- /tests/testthat/apples.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/tests/testthat/apples.pdf -------------------------------------------------------------------------------- /tests/testthat/helper-async.R: -------------------------------------------------------------------------------- 1 | # Given a promise-yielding expression, loop until it resolves or rejects. 2 | # DON'T USE THIS TECHNIQUE IN SHINY, PLUMBER, OR HTTPUV CONTEXTS. 3 | sync <- function(expr) { 4 | p <- force(expr) 5 | 6 | done <- FALSE 7 | success <- NULL 8 | error <- NULL 9 | 10 | promises::then( 11 | p, 12 | function(result) { 13 | success <<- result 14 | done <<- TRUE 15 | }, 16 | function(err) { 17 | error <<- err 18 | done <<- TRUE 19 | } 20 | ) 21 | 22 | while (!done) { 23 | later::run_now(0.25) 24 | } 25 | if (!is.null(error)) { 26 | stop(error) 27 | } else { 28 | success 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/testthat/helper-chat.R: -------------------------------------------------------------------------------- 1 | MockedChat <- R6::R6Class( 2 | "MockedChat", 3 | inherit = Chat, 4 | public = list( 5 | i = 0, 6 | saved_chats = character(), 7 | 8 | initialize = function(saved_chats) { 9 | self$saved_chats <- saved_chats 10 | }, 11 | 12 | chat = function(...) { 13 | self$i <- self$i + 1 14 | self$saved_chats[self$i] 15 | } 16 | ) 17 | ) 18 | 19 | mocked_chat <- function(chats) { 20 | MockedChat$new(saved_chats = chats) 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/helper-content-tools.R: -------------------------------------------------------------------------------- 1 | fixture_list_of_tools <- function() { 2 | list( 3 | tool_scalar = tool(function() 1, "Tool", .name = "tool_scalar"), 4 | my_tool = tool(function() 1, "Tool", .name = "my_tool"), 5 | tool_list = tool( 6 | function() list(a = 1, b = 2), 7 | "Tool", 8 | .name = "tool_list" 9 | ), 10 | tool_chr = tool(function() letters[1:3], "Tool", .name = "tool_chr"), 11 | tool_abort = tool(.description = "Tool", .name = "tool_abort", function() { 12 | cli::cli_abort(c( 13 | "Unexpected input", 14 | "i" = "Please revise and try again." 15 | )) 16 | }) 17 | ) 18 | } 19 | 20 | fixture_turn_with_tool_requests <- function(with_tool = TRUE) { 21 | tools <- fixture_list_of_tools() 22 | 23 | req_success <- ContentToolRequest( 24 | id = "x1", 25 | name = "my_tool", 26 | arguments = list(), 27 | tool = if (with_tool) tools$my_tool 28 | ) 29 | req_fail <- ContentToolRequest( 30 | id = "x2", 31 | name = "my_tool", 32 | arguments = list(x = 1), 33 | tool = if (with_tool) tools$my_tool 34 | ) 35 | req_list <- ContentToolRequest( 36 | id = "x3", 37 | name = "tool_list", 38 | arguments = list(), 39 | tool = if (with_tool) tools$tool_list 40 | ) 41 | req_chr <- ContentToolRequest( 42 | id = "x4", 43 | name = "tool_chr", 44 | arguments = list(), 45 | tool = if (with_tool) tools$tool_chr 46 | ) 47 | req_abort <- ContentToolRequest( 48 | id = "x5", 49 | name = "tool_abort", 50 | arguments = list(), 51 | tool = if (with_tool) tools$tool_abort 52 | ) 53 | 54 | Turn( 55 | "assistant", 56 | list(req_success, req_fail, req_list, req_chr, req_abort) 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /tests/testthat/teardown-usage.R: -------------------------------------------------------------------------------- 1 | token_usage() 2 | -------------------------------------------------------------------------------- /tests/testthat/test-as-json.R: -------------------------------------------------------------------------------- 1 | test_that("can convert basic types to json schema", { 2 | expect_equal( 3 | as_json(test_provider(), type_boolean("desc")), 4 | list(type = "boolean", description = "desc") 5 | ) 6 | 7 | expect_equal( 8 | as_json(test_provider(), type_enum("desc", letters[1:3])), 9 | list(type = "string", description = "desc", enum = as.list(letters[1:3])) 10 | ) 11 | 12 | expect_equal( 13 | as_json(test_provider(), type_array("a", type_boolean("b"))), 14 | list( 15 | type = "array", 16 | description = "a", 17 | items = list(type = "boolean", description = "b") 18 | ) 19 | ) 20 | }) 21 | 22 | test_that("can convert an object to json schema", { 23 | obj <- type_object( 24 | "a", 25 | integer = type_integer(), 26 | number = type_number(), 27 | string = type_string() 28 | ) 29 | 30 | expect_equal( 31 | as_json(test_provider(), obj), 32 | list( 33 | type = "object", 34 | description = "a", 35 | properties = list( 36 | integer = list(type = "integer", description = ""), 37 | number = list(type = "number", description = ""), 38 | string = list(type = "string", description = "") 39 | ), 40 | required = list("integer", "number", "string"), 41 | additionalProperties = FALSE 42 | ) 43 | ) 44 | }) 45 | -------------------------------------------------------------------------------- /tests/testthat/test-content-image.R: -------------------------------------------------------------------------------- 1 | test_that("can create image from url", { 2 | obj <- content_image_url("https://www.r-project.org/Rlogo.png") 3 | expect_s3_class(obj, "ellmer::ContentImageRemote") 4 | }) 5 | 6 | test_that("can create inline image from data url", { 7 | obj <- content_image_url("data:image/png;base64,abcd") 8 | expect_s3_class(obj, "ellmer::ContentImageInline") 9 | expect_equal(obj@type, "image/png") 10 | expect_equal(obj@data, "abcd") 11 | }) 12 | 13 | test_that("errors with invalid data urls", { 14 | expect_snapshot(content_image_url("data:base64,abcd"), error = TRUE) 15 | 16 | expect_error(content_image_url("data:")) 17 | expect_error(content_image_url("data:;;;")) 18 | expect_error(content_image_url("data:image/png;abc")) 19 | }) 20 | 21 | test_that("can create image from path", { 22 | skip_if_not_installed("magick") 23 | 24 | path <- system.file("httr2.png", package = "ellmer") 25 | obj <- content_image_file(path) 26 | expect_s3_class(obj, "ellmer::ContentImageInline") 27 | }) 28 | 29 | test_that("can create image from plot", { 30 | withr::local_pdf(NULL) 31 | dev.control("enable") 32 | plot(1:10) 33 | 34 | obj <- content_image_plot() 35 | expect_s3_class(obj, "ellmer::ContentImageInline") 36 | expect_equal(obj@type, "image/png") 37 | }) 38 | 39 | test_that("image resizing", { 40 | skip_if_not_installed("magick") 41 | 42 | img_file <- system.file("httr2.png", package = "ellmer") 43 | 44 | expect_snapshot(error = TRUE, { 45 | content_image_file("DOESNTEXIST") 46 | content_image_file(test_path("test-content.R")) 47 | content_image_file(img_file, resize = TRUE) 48 | content_image_file(img_file, resize = "blah") 49 | }) 50 | 51 | expect_no_error(content_image_file(img_file)) 52 | expect_no_error(content_image_file(img_file, resize = "low")) 53 | expect_no_error(content_image_file(img_file, resize = "high")) 54 | expect_no_error(content_image_file(img_file, resize = "none")) 55 | expect_no_error(content_image_file(img_file, resize = "100x100")) 56 | expect_no_error(content_image_file(img_file, resize = "100x100>!")) 57 | }) 58 | 59 | test_that("useful errors if no display", { 60 | # file based devices have display list inhibited 61 | withr::local_pdf(NULL) 62 | expect_snapshot(content_image_plot(), error = TRUE) 63 | }) 64 | -------------------------------------------------------------------------------- /tests/testthat/test-content-pdf.R: -------------------------------------------------------------------------------- 1 | test_that("can create pdf from path", { 2 | obj <- content_pdf_file(test_path("apples.pdf")) 3 | expect_s3_class(obj, "ellmer::ContentPDF") 4 | }) 5 | -------------------------------------------------------------------------------- /tests/testthat/test-content.R: -------------------------------------------------------------------------------- 1 | test_that("invalid inputs give useful errors", { 2 | chat <- chat_openai_test() 3 | 4 | expect_snapshot(error = TRUE, { 5 | chat$chat(question = "Are unicorns real?") 6 | chat$chat(TRUE) 7 | }) 8 | }) 9 | 10 | test_that("can create content from a vector", { 11 | expect_equal( 12 | as_content(c("a", "b")), 13 | ContentText("a\n\nb") 14 | ) 15 | }) 16 | 17 | test_that("turn contents can be converted to text, markdown and HTML", { 18 | turn <- Turn( 19 | "user", 20 | contents = list( 21 | ContentText("User input."), 22 | ContentImageInline("image/png", "abcd123"), 23 | ContentImageRemote("https://example.com/image.jpg", detail = ""), 24 | ContentJson(list(a = 1:2, b = "apple")), 25 | ContentSql("SELECT * FROM mtcars"), 26 | ContentSuggestions( 27 | c( 28 | "What is the total quantity sold for each product last quarter?", 29 | "What is the average discount percentage for orders from the United States?", 30 | "What is the average price of products in the 'electronics' category?" 31 | ) 32 | ) 33 | ) 34 | ) 35 | 36 | expect_snapshot(cat(contents_text(turn))) 37 | expect_snapshot(cat(contents_markdown(turn))) 38 | 39 | turns <- list( 40 | turn, 41 | Turn("assistant", list(ContentText("Here's your answer."))) 42 | ) 43 | chat <- Chat$new(test_provider()) 44 | chat$set_turns(turns) 45 | expect_snapshot(cat(contents_markdown(chat))) 46 | 47 | skip_if_not_installed("commonmark") 48 | expect_snapshot(cat(contents_html(turn))) 49 | }) 50 | 51 | 52 | # Content types ---------------------------------------------------------------- 53 | 54 | test_that("thinking has useful representations", { 55 | ct <- ContentThinking("A **thought**.") 56 | expect_equal(contents_text(ct), NULL) 57 | expect_equal(format(ct), "\nA **thought**.\n\n") 58 | expect_equal( 59 | contents_markdown(ct), 60 | "\nA **thought**.\n\n" 61 | ) 62 | expect_snapshot(cat(contents_html(ct))) 63 | }) 64 | 65 | test_that("ContentToolResult@error requires a string or an error condition", { 66 | expect_snapshot(error = TRUE, { 67 | ContentToolResult("id", error = TRUE) 68 | ContentToolResult("id", error = c("one", "two")) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /tests/testthat/test-deprecated.R: -------------------------------------------------------------------------------- 1 | test_that("chat_azure() is deprecated", { 2 | lifecycle::expect_deprecated( 3 | chat_azure("foo", "bar", api_key = "key"), 4 | "chat_azure_openai" 5 | ) 6 | }) 7 | 8 | test_that("chat_bedrock() is deprecated", { 9 | local_mocked_bindings( 10 | aws_creds_cache = function(...) list(), 11 | paws_credentials = function(...) list(region = "us-east-1") 12 | ) 13 | 14 | lifecycle::expect_deprecated( 15 | chat_bedrock(), 16 | "chat_aws_bedrock" 17 | ) 18 | }) 19 | 20 | test_that("chat_claude() is deprecated", { 21 | lifecycle::expect_deprecated( 22 | chat_claude(api_key = "key"), 23 | "chat_anthropic" 24 | ) 25 | }) 26 | 27 | test_that("chat_gemini() is deprecated", { 28 | lifecycle::expect_deprecated( 29 | chat_gemini(api_key = "key"), 30 | "chat_google_gemini" 31 | ) 32 | }) 33 | -------------------------------------------------------------------------------- /tests/testthat/test-interpolate.R: -------------------------------------------------------------------------------- 1 | test_that("checks inputs", { 2 | expect_snapshot(error = TRUE, { 3 | interpolate(1) 4 | interpolate("x", 1) 5 | }) 6 | }) 7 | 8 | test_that("vectorised interpolation generates a list", { 9 | expect_equal( 10 | interpolate("{{x}}", x = 1:2), 11 | ellmer_prompt(c("1", "2")) 12 | ) 13 | }) 14 | 15 | test_that("has a nice print method", { 16 | expect_snapshot(interpolate("Hi!")) 17 | }) 18 | 19 | test_that("print method truncates many elements", { 20 | prompt <- ellmer_prompt(c("x\ny", c("a\nb\nc\nd\ne"))) 21 | expect_snapshot({ 22 | print(prompt, max_items = 1) 23 | print(prompt, max_lines = 2) 24 | print(prompt, max_lines = 3) 25 | }) 26 | }) 27 | 28 | test_that("can interpolate from local env or from ...", { 29 | x <- 1 30 | 31 | expect_equal(interpolate("{{x}}"), ellmer_prompt("1")) 32 | expect_equal(interpolate("{{x}}", x = 2), ellmer_prompt("2")) 33 | }) 34 | 35 | test_that("can take a data frame via !!!", { 36 | df <- data.frame(x = 1, y = 2) 37 | expect_equal(interpolate("{{x}} + {{y}}", !!!df), ellmer_prompt("1 + 2")) 38 | }) 39 | 40 | test_that("can interpolate from a file", { 41 | path <- withr::local_tempfile(lines = "{{x}}") 42 | expect_equal(interpolate_file(path, x = 1), ellmer_prompt("1")) 43 | }) 44 | 45 | test_that("can interpolate from a package", { 46 | path <- withr::local_tempfile(lines = "{{x}}") 47 | local_mocked_bindings( 48 | system.file = function(..., package = "base") { 49 | if (package == "test") path else stop("package not found") 50 | } 51 | ) 52 | 53 | expect_equal(interpolate_package("test", "bar.md", x = 1), ellmer_prompt("1")) 54 | }) 55 | -------------------------------------------------------------------------------- /tests/testthat/test-params.R: -------------------------------------------------------------------------------- 1 | # params ------------------------------------------------------------------- 2 | 3 | test_that("NULL values are stripped", { 4 | expect_equal(params(), set_names(list())) 5 | }) 6 | 7 | test_that("checks its inputs", { 8 | expect_snapshot(error = TRUE, { 9 | params(temperature = "x") 10 | params(top_p = "x") 11 | params(top_k = "x") 12 | params(frequency_penalty = "x") 13 | params(presence_penalty = "x") 14 | params(seed = "x") 15 | params(max_tokens = "x") 16 | params(log_probs = 1) 17 | params(stop_sequences = 1) 18 | }) 19 | }) 20 | 21 | # standardise_params ------------------------------------------------------- 22 | 23 | test_that("standardise_params warns about unknown args", { 24 | test_params <- list(temperature = 0.7, top_p = 0.9) 25 | provider_params <- c("temperature" = "temperature") 26 | expect_snapshot(. <- standardise_params(test_params, provider_params)) 27 | }) 28 | 29 | test_that("standardise_params renames supported parameters", { 30 | test_params <- list( 31 | top_p = 0.9, 32 | temperature = 0.7, 33 | max_tokens = 100 34 | ) 35 | 36 | provider_params <- c( 37 | temp = "temperature", 38 | topP = "top_p", 39 | maxTokens = "max_tokens" 40 | ) 41 | expect_equal( 42 | standardise_params(test_params, provider_params), 43 | list(topP = 0.9, temp = 0.7, maxTokens = 100) 44 | ) 45 | }) 46 | 47 | test_that("standardise_params handles empty parameters correctly", { 48 | test_params <- list(extra_args = list()) 49 | provider_params <- c("temperature" = "temperature", "top_p" = "top_p") 50 | expect_equal(standardise_params(test_params, provider_params), list()) 51 | }) 52 | 53 | test_that("standardise_params leavees extra_args as is", { 54 | test_params <- list(top_k = 2, extra_args = list(a = 1, b = 2)) 55 | 56 | provider_params <- c(top_k = "top_k") 57 | expect_equal( 58 | standardise_params(test_params, provider_params), 59 | list(top_k = 2, a = 1, b = 2) 60 | ) 61 | }) 62 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-claude.R: -------------------------------------------------------------------------------- 1 | test_that("can make simple batch request", { 2 | chat <- chat_anthropic_test("Be as terse as possible; no punctuation") 3 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 4 | expect_match(resp, "2") 5 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 6 | }) 7 | 8 | test_that("can make simple streaming request", { 9 | chat <- chat_anthropic_test("Be as terse as possible; no punctuation") 10 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 11 | expect_match(paste0(unlist(resp), collapse = ""), "2") 12 | }) 13 | 14 | test_that("can list models", { 15 | test_models(models_anthropic) 16 | }) 17 | 18 | # Common provider interface ----------------------------------------------- 19 | 20 | test_that("defaults are reported", { 21 | expect_snapshot(. <- chat_anthropic()) 22 | }) 23 | 24 | test_that("supports standard parameters", { 25 | chat_fun <- chat_anthropic_test 26 | 27 | test_params_stop(chat_fun) 28 | }) 29 | 30 | test_that("all tool variations work", { 31 | chat_fun <- chat_anthropic_test 32 | 33 | retry_test(test_tools_simple(chat_fun)) 34 | test_tools_async(chat_fun) 35 | test_tools_parallel(chat_fun) 36 | # Claude sometimes returns an empty string 37 | retry_test(test_tools_sequential(chat_fun, total_calls = 6)) 38 | }) 39 | 40 | test_that("can extract data", { 41 | chat_fun <- chat_anthropic_test 42 | 43 | test_data_extraction(chat_fun) 44 | }) 45 | 46 | test_that("can use images", { 47 | chat_fun <- chat_anthropic_test 48 | 49 | test_images_inline(chat_fun) 50 | test_images_remote(chat_fun) 51 | }) 52 | 53 | test_that("can use pdfs", { 54 | chat_fun <- chat_anthropic_test 55 | 56 | test_pdf_local(chat_fun) 57 | }) 58 | 59 | # Custom features -------------------------------------------------------- 60 | 61 | test_that("can set beta headers", { 62 | chat <- chat_anthropic_test(beta_headers = c("a", "b")) 63 | req <- chat_request(chat$get_provider()) 64 | expect_equal(req$headers$`anthropic-beta`, c("a", "b")) 65 | }) 66 | 67 | test_that("continues to work after whitespace only outputs (#376)", { 68 | chat <- chat_anthropic() 69 | chat$chat("Respond with only two blank lines") 70 | expect_equal(chat$chat("What's 1+1? Just give me the number"), "2") 71 | }) 72 | 73 | test_that("max_tokens is deprecated", { 74 | expect_snapshot(chat <- chat_anthropic_test(max_tokens = 10)) 75 | expect_equal(chat$get_provider()@params$max_tokens, 10) 76 | }) 77 | 78 | test_that("strips suffix from model name", { 79 | provider <- ProviderAnthropic("", model = "", base_url = "", api_key = "") 80 | expect_equal( 81 | standardise_model(provider, "claude-3-7-sonnet-latest"), 82 | "claude-3-7-sonnet" 83 | ) 84 | expect_equal( 85 | standardise_model(provider, "claude-3-7-sonnet-20250219"), 86 | "claude-3-7-sonnet" 87 | ) 88 | }) 89 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-cloudflare.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_cloudflare_test("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_cloudflare_test("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | # Common provider interface ----------------------------------------------- 17 | 18 | test_that("defaults are reported", { 19 | expect_snapshot(. <- chat_cloudflare()) 20 | }) 21 | 22 | # Not supported 23 | # test_that("supports standard parameters", { 24 | # chat_fun <- chat_cloudflare_test 25 | 26 | # test_params_stop(chat_fun) 27 | # }) 28 | 29 | # Doesn't appear to work 30 | # test_that("all tool variations work", { 31 | # chat_fun <- function(...) { 32 | # chat_cloudflare_test( 33 | # ..., 34 | # model = "@hf/nousresearch/hermes-2-pro-mistral-7b" 35 | # ) 36 | # } 37 | 38 | # test_tools_simple(chat_fun) 39 | # test_tools_async(chat_fun) 40 | # test_tools_parallel(chat_fun) 41 | # test_tools_sequential(chat_fun, total_calls = 6) 42 | # }) 43 | 44 | test_that("can extract data", { 45 | chat_fun <- chat_cloudflare_test 46 | 47 | test_data_extraction(chat_fun) 48 | }) 49 | 50 | # Can't find model that works 51 | # test_that("can use images", { 52 | # chat_fun <- function(...) 53 | # chat_cloudflare_test(model = "@cf/llava-hf/llava-1.5-7b-hf", ...) 54 | 55 | # test_images_inline(chat_fun) 56 | # # test_images_remote(chat_fun) 57 | # }) 58 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-deepseek.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_deepseek("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_deepseek("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | # Common provider interface ----------------------------------------------- 17 | 18 | test_that("defaults are reported", { 19 | expect_snapshot(. <- chat_deepseek()) 20 | }) 21 | 22 | test_that("all tool variations work", { 23 | chat_fun <- chat_deepseek 24 | 25 | test_tools_simple(chat_fun) 26 | test_tools_async(chat_fun) 27 | test_tools_parallel(chat_fun) 28 | test_tools_sequential(chat_fun, total_calls = 6) 29 | }) 30 | 31 | # # Doesn't support data extraction 32 | # test_that("can extract data", { 33 | # chat_fun <- chat_deepseek 34 | 35 | # test_data_extraction(chat_fun) 36 | # }) 37 | 38 | # # Doesn't support images 39 | # test_that("can use images", { 40 | # chat_fun <- chat_deepseek_test 41 | 42 | # test_images_inline(chat_fun) 43 | # test_images_remote(chat_fun) 44 | # }) 45 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-gemini-upload.R: -------------------------------------------------------------------------------- 1 | test_that("can upload a file an ask questions about it", { 2 | upload <- google_upload(test_path("apples.pdf")) 3 | 4 | chat <- chat_google_gemini() 5 | response <- chat$chat("What's the title of this document?", upload) 6 | expect_match(response, "Apples are tasty") 7 | expect_match(chat$chat("What apple is not tasty?"), "red delicious") 8 | }) 9 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-github.R: -------------------------------------------------------------------------------- 1 | test_that("uses to GITHUB_PAT if set", { 2 | withr::local_envvar( 3 | GITHUB_PAT = "abc", 4 | GITHUB_PAT_GITHUB_COM = NA 5 | ) 6 | expect_equal(github_key(), "abc") 7 | }) 8 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-groq.R: -------------------------------------------------------------------------------- 1 | # Common provider interface ----------------------------------------------- 2 | 3 | test_that("defaults are reported", { 4 | expect_snapshot(. <- chat_groq()) 5 | }) 6 | 7 | test_that("all tool variations work", { 8 | chat_fun <- function(...) chat_groq(..., model = "Llama-3.3-70b-Versatile") 9 | 10 | test_tools_simple(chat_fun) 11 | test_tools_async(chat_fun) 12 | test_tools_parallel(chat_fun) 13 | test_tools_sequential(chat_fun, total_calls = 6) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-huggingface.R: -------------------------------------------------------------------------------- 1 | test_that("can make simple request", { 2 | chat <- chat_huggingface_test("Be as terse as possible; no punctuation") 3 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 4 | expect_match(resp, "2") 5 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 6 | }) 7 | 8 | test_that("can make simple streaming request", { 9 | chat <- chat_huggingface_test("Be as terse as possible; no punctuation") 10 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 11 | expect_match(paste0(unlist(resp), collapse = ""), "2") 12 | }) 13 | 14 | # Common provider interface ----------------------------------------------- 15 | 16 | test_that("defaults are reported", { 17 | expect_snapshot(. <- chat_huggingface_test()) 18 | }) 19 | 20 | # Stop tokens don't appear to work correctly 21 | # test_that("supports standard parameters", { 22 | # chat_fun <- chat_huggingface_test 23 | 24 | # test_params_stop(chat_fun) 25 | # }) 26 | 27 | # Gets stuck in infinite loop: https://github.com/huggingface/text-generation-inference/issues/2986 28 | # test_that("all tool variations work", { 29 | # chat_fun <- chat_huggingface_test 30 | 31 | # test_tools_simple(chat_fun) 32 | # test_tools_async(chat_fun) 33 | # test_tools_parallel(chat_fun) 34 | # test_tools_sequential(chat_fun, total_calls = 6) 35 | # }) 36 | 37 | # Can't find model that does a good job 38 | # test_that("can extract data", { 39 | # chat_fun <- chat_huggingface_test 40 | 41 | # test_data_extraction(chat_fun) 42 | # }) 43 | 44 | # Can't find model that does a good job 45 | # test_that("can use images", { 46 | # chat_fun <- function(...) 47 | # chat_huggingface_test(model = "Qwen/Qwen2.5-VL-7B-Instruct") 48 | 49 | # # Thinks hexagon is a diamond 50 | # test_images_inline(chat_fun, test_shape = FALSE) 51 | # test_images_remote(chat_fun, test_shape = FALSE) 52 | # }) 53 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-mistral.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_mistral_test("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_mistral_test("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | test_that("can handle errors", { 17 | chat <- chat_mistral_test(model = "doesnt-exist") 18 | expect_snapshot(chat$chat("Hi"), error = TRUE) 19 | }) 20 | 21 | # Common provider interface ----------------------------------------------- 22 | 23 | test_that("defaults are reported", { 24 | expect_snapshot(. <- chat_mistral()) 25 | }) 26 | 27 | test_that("supports standard parameters", { 28 | chat_fun <- chat_mistral_test 29 | 30 | test_params_stop(chat_fun) 31 | }) 32 | 33 | # Tool calling is poorly supported 34 | # test_that("all tool variations work", { 35 | # chat_fun <- chat_mistral_test 36 | 37 | # test_tools_simple(chat_fun) 38 | # test_tools_async(chat_fun) 39 | # test_tools_parallel(chat_fun) 40 | # test_tools_sequential(chat_fun, total_calls = 6) 41 | # }) 42 | 43 | test_that("can extract data", { 44 | chat_fun <- chat_mistral_test 45 | 46 | test_data_extraction(chat_fun) 47 | }) 48 | 49 | test_that("can use images", { 50 | chat_fun <- \(...) chat_mistral_test(model = "pixtral-12b-latest") 51 | 52 | test_images_inline(chat_fun) 53 | test_images_remote(chat_fun) 54 | }) 55 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-ollama.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_ollama_test("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_ollama_test("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | test_that("can list models", { 17 | skip_if_no_ollama() 18 | test_models(models_ollama) 19 | }) 20 | 21 | test_that("includes list of models in error message if `model` is missing", { 22 | skip_if_no_ollama() 23 | 24 | local_mocked_bindings( 25 | models_ollama = function(...) list(id = "llama3") 26 | ) 27 | 28 | expect_snapshot(chat_ollama(), error = TRUE) 29 | }) 30 | 31 | test_that("checks that requested model is installed", { 32 | skip_if_no_ollama() 33 | local_mocked_bindings( 34 | models_ollama = function(...) list(id = "llama3") 35 | ) 36 | expect_snapshot( 37 | chat_ollama(model = "not-a-real-model"), 38 | error = TRUE 39 | ) 40 | }) 41 | 42 | # Common provider interface ----------------------------------------------- 43 | 44 | test_that("can chat with tool request", { 45 | chat <- chat_ollama_test("Be as terse as possible; no punctuation") 46 | 47 | add_two_numbers <- function(x, y = 0) x + y 48 | chat$register_tool( 49 | tool( 50 | add_two_numbers, 51 | "Add two numbers together.", 52 | x = type_number("The first number"), 53 | y = type_number("The second number", required = FALSE) 54 | ) 55 | ) 56 | 57 | # Tool with no properties 58 | current_time <- function() Sys.time() 59 | chat$register_tool(tool(current_time, "Current system time")) 60 | 61 | # Ollama tool calling is very inconsistent, esp. with small models, so we 62 | # just test that the model still works when a tool call is registered. 63 | expect_no_error( 64 | coro::collect(chat$stream("What is 1 + 1?")) 65 | ) 66 | }) 67 | 68 | # Currently no other tests because I can't find a model that returns reliable 69 | # results and is reasonably performant. 70 | 71 | # Custom ----------------------------------------------------------------- 72 | 73 | test_that("as_json specialised for Ollama", { 74 | stub <- ProviderOllama(name = "", base_url = "", api_key = "", model = "") 75 | 76 | expect_snapshot( 77 | as_json(stub, type_object(.additional_properties = TRUE)), 78 | error = TRUE 79 | ) 80 | 81 | obj <- type_object( 82 | x = type_number(required = FALSE), 83 | y = type_string(required = TRUE) 84 | ) 85 | expect_equal( 86 | as_json(stub, obj), 87 | list( 88 | type = "object", 89 | description = "", 90 | properties = list( 91 | x = list(type = c("number"), description = ""), 92 | y = list(type = c("string"), description = "") 93 | ), 94 | required = list("y"), 95 | additionalProperties = FALSE 96 | ) 97 | ) 98 | }) 99 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-openai.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_openai_test() 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_openai_test() 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | test_that("can list models", { 17 | test_models(models_openai) 18 | }) 19 | 20 | 21 | # Common provider interface ----------------------------------------------- 22 | 23 | test_that("defaults are reported", { 24 | expect_snapshot(. <- chat_openai()) 25 | }) 26 | 27 | test_that("supports standard parameters", { 28 | chat_fun <- chat_openai_test 29 | 30 | test_params_stop(chat_fun) 31 | }) 32 | 33 | test_that("all tool variations work", { 34 | chat_fun <- chat_openai_test 35 | 36 | test_tools_simple(chat_fun) 37 | test_tools_async(chat_fun) 38 | test_tools_parallel(chat_fun) 39 | test_tools_sequential(chat_fun, total_calls = 6) 40 | }) 41 | 42 | test_that("can extract data", { 43 | chat_fun <- chat_openai_test 44 | 45 | test_data_extraction(chat_fun) 46 | }) 47 | 48 | test_that("can use images", { 49 | # Needs mini to get shape correct 50 | chat_fun <- \(...) chat_openai_test(model = "gpt-4.1-mini", ...) 51 | 52 | test_images_inline(chat_fun) 53 | test_images_remote(chat_fun) 54 | }) 55 | 56 | # Custom tests ----------------------------------------------------------------- 57 | 58 | test_that("can retrieve log_probs (#115)", { 59 | chat <- chat_openai_test(params = params(log_probs = TRUE)) 60 | pieces <- coro::collect(chat$stream("Hi")) 61 | 62 | logprobs <- chat$last_turn()@json$choices[[1]]$logprobs$content 63 | expect_equal( 64 | length(logprobs), 65 | length(pieces) - 2 # leading "" + trailing \n 66 | ) 67 | }) 68 | 69 | # Custom ----------------------------------------------------------------- 70 | 71 | test_that("as_json specialised for OpenAI", { 72 | stub <- ProviderOpenAI(name = "", base_url = "", api_key = "", model = "") 73 | 74 | expect_snapshot( 75 | as_json(stub, type_object(.additional_properties = TRUE)), 76 | error = TRUE 77 | ) 78 | 79 | obj <- type_object(x = type_number(required = FALSE)) 80 | expect_equal( 81 | as_json(stub, obj), 82 | list( 83 | type = "object", 84 | description = "", 85 | properties = list(x = list(type = c("number", "null"), description = "")), 86 | required = list("x"), 87 | additionalProperties = FALSE 88 | ) 89 | ) 90 | }) 91 | 92 | test_that("seed is deprecated, but still honored", { 93 | expect_snapshot(chat <- chat_openai_test(seed = 1)) 94 | expect_equal(chat$get_provider()@params$seed, 1) 95 | }) 96 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-openrouter.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_openrouter_test("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_openrouter_test("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | test_that("handles errors", { 17 | chat <- chat_openrouter_test(api_args = list(temperature = "hot")) 18 | expect_snapshot(error = TRUE, { 19 | chat$chat("What is 1 + 1?", echo = FALSE) 20 | chat$chat("What is 1 + 1?", echo = TRUE) 21 | }) 22 | }) 23 | 24 | # Common provider interface ----------------------------------------------- 25 | 26 | test_that("all tool variations work", { 27 | chat_fun <- chat_openrouter_test 28 | 29 | test_tools_simple(chat_fun) 30 | test_tools_async(chat_fun) 31 | test_tools_parallel(chat_fun) 32 | test_tools_sequential(chat_fun, total_calls = 6) 33 | }) 34 | 35 | test_that("can extract data", { 36 | chat_fun <- chat_openrouter_test 37 | 38 | test_data_extraction(chat_fun) 39 | }) 40 | 41 | test_that("can use images", { 42 | chat_fun <- chat_openrouter_test 43 | 44 | test_images_inline(chat_fun) 45 | test_images_remote(chat_fun) 46 | }) 47 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-portkey.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_portkey_test( 5 | virtual_key = Sys.getenv("PORTKEY_VIRTUAL_KEY"), 6 | "Be as terse as possible; no punctuation" 7 | ) 8 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 9 | expect_match(resp, "2") 10 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 11 | }) 12 | 13 | test_that("can make simple streaming request", { 14 | chat <- chat_portkey_test( 15 | virtual_key = Sys.getenv("PORTKEY_VIRTUAL_KEY"), 16 | "Be as terse as possible; no punctuation" 17 | ) 18 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 19 | expect_match(paste0(unlist(resp), collapse = ""), "2") 20 | }) 21 | 22 | # Common provider interface ----------------------------------------------- 23 | 24 | test_that("defaults are reported", { 25 | expect_snapshot(. <- chat_portkey()) 26 | }) 27 | 28 | test_that("all tool variations work", { 29 | chat_fun <- \(...) { 30 | chat_portkey_test(virtual_key = Sys.getenv("PORTKEY_VIRTUAL_KEY")) 31 | } 32 | 33 | test_tools_simple(chat_fun) 34 | test_tools_async(chat_fun) 35 | test_tools_parallel(chat_fun) 36 | test_tools_sequential(chat_fun, total_calls = 6) 37 | }) 38 | 39 | test_that("can extract data", { 40 | chat_fun <- \(...) { 41 | chat_portkey_test(virtual_key = Sys.getenv("PORTKEY_VIRTUAL_KEY")) 42 | } 43 | 44 | test_data_extraction(chat_fun) 45 | }) 46 | 47 | test_that("can use images", { 48 | chat_fun <- \(...) { 49 | chat_portkey_test( 50 | virtual_key = Sys.getenv("PORTKEY_VIRTUAL_KEY"), 51 | model = "gpt-4.1-mini", 52 | ... 53 | ) 54 | } 55 | 56 | test_images_inline(chat_fun) 57 | test_images_remote(chat_fun) 58 | }) 59 | -------------------------------------------------------------------------------- /tests/testthat/test-provider-vllm.R: -------------------------------------------------------------------------------- 1 | # Getting started -------------------------------------------------------- 2 | 3 | test_that("can make simple request", { 4 | chat <- chat_vllm_test("Be as terse as possible; no punctuation") 5 | resp <- chat$chat("What is 1 + 1?", echo = FALSE) 6 | expect_match(resp, "2") 7 | expect_equal(chat$last_turn()@tokens > 0, c(TRUE, TRUE)) 8 | }) 9 | 10 | test_that("can make simple streaming request", { 11 | chat <- chat_vllm_test("Be as terse as possible; no punctuation") 12 | resp <- coro::collect(chat$stream("What is 1 + 1?")) 13 | expect_match(paste0(unlist(resp), collapse = ""), "2") 14 | }) 15 | 16 | test_that("can list models", { 17 | test_models(\(...) models_vllm("https://llm.nrp-nautilus.io/")) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testthat/test-tokens.R: -------------------------------------------------------------------------------- 1 | test_that("useful message if no tokens", { 2 | local_tokens() 3 | 4 | expect_snapshot(token_usage()) 5 | }) 6 | 7 | test_that("can retrieve and log tokens", { 8 | local_tokens() 9 | provider <- test_provider("testprovider", "test") 10 | 11 | expect_equal(tokens_log(provider, 10, 50), c(10, 50)) 12 | expect_equal(the$tokens, tokens_row("testprovider", "test", 10, 50)) 13 | 14 | expect_equal(tokens_log(provider, 0, 10), c(0, 10)) 15 | expect_equal(the$tokens, tokens_row("testprovider", "test", 10, 60)) 16 | 17 | expect_equal(tokens_log(provider), c(0, 0)) 18 | expect_equal(the$tokens, tokens_row("testprovider", "test", 10, 60)) 19 | 20 | expect_snapshot(token_usage()) 21 | }) 22 | 23 | test_that("can compute price of tokens", { 24 | expect_equal(get_token_cost("OpenAI", "gpt-4o", 1e6, 0), dollars(2.5)) 25 | expect_equal(get_token_cost("OpenAI", "gpt-4o", 0, 1e6), dollars(10)) 26 | }) 27 | 28 | test_that("token_usage() shows price if available", { 29 | local_tokens() 30 | local_mocked_bindings( 31 | prices = data.frame( 32 | provider = "testprovider", 33 | model = "test", 34 | input = 0.10, 35 | output = 0.01 36 | ) 37 | ) 38 | provider <- test_provider("testprovider", "test") 39 | 40 | tokens_log(provider, 123e5, 678e3) 41 | expect_snapshot(token_usage()) 42 | }) 43 | 44 | test_that("price is formatted nicely", { 45 | expect_equal(format(dollars(NA)), "NA") 46 | expect_equal(format(dollars(0.0001)), "$0.00") 47 | expect_equal(format(dollars(c(10, 1))), c("$10.00", "$ 1.00")) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-tools-def-auto.R: -------------------------------------------------------------------------------- 1 | test_that("help topic extraction works", { 2 | print_help <- get_help_text("print") 3 | expect_type(print_help, "character") 4 | expect_gt(nchar(print_help), 50) 5 | 6 | expect_identical(get_help_text("print", "base"), print_help) 7 | }) 8 | 9 | test_that("roxygen2 comment extraction works", { 10 | sys.source(test_path("tools-def.R"), environment(), keep.source = TRUE) 11 | aliased_function <- has_roxygen_comments 12 | 13 | expect_snapshot(extract_comments_and_signature(has_roxygen_comments)) 14 | expect_snapshot(extract_comments_and_signature(aliased_function)) 15 | expect_snapshot(extract_comments_and_signature(indented_comments)) 16 | expect_snapshot(extract_comments_and_signature(no_srcfile)) 17 | }) 18 | 19 | test_that("basic signature extraction works", { 20 | sys.source(test_path("tools-def.R"), environment(), keep.source = TRUE) 21 | expect_snapshot(extract_comments_and_signature(no_roxygen_comments)) 22 | }) 23 | 24 | test_that("checks its inputs", { 25 | expect_snapshot(error = TRUE, { 26 | create_tool_def(print, model = "gpt-4", chat = chat_google_gemini()) 27 | create_tool_def(print, chat = 1) 28 | }) 29 | }) 30 | 31 | test_that("model is deprecated", { 32 | mock <- mocked_chat("response") 33 | local_mocked_bindings(chat_openai = function(...) mock) 34 | 35 | expect_snapshot( 36 | . <- create_tool_def(print, model = "gpt-4", echo = FALSE) 37 | ) 38 | }) 39 | -------------------------------------------------------------------------------- /tests/testthat/test-tools-def.R: -------------------------------------------------------------------------------- 1 | test_that("tool can get name", { 2 | f <- function() { 3 | } 4 | td <- tool(f, "") 5 | expect_equal(td@name, "f") 6 | 7 | td <- tool( 8 | function() { 9 | }, 10 | "" 11 | ) 12 | expect_match(td@name, "^tool_") 13 | }) 14 | 15 | test_that("json_schema_parameters generates correct paramters if no arguments", { 16 | expect_equal( 17 | as_json(test_provider(), type_object()), 18 | list( 19 | type = "object", 20 | description = "", 21 | properties = set_names(list()), 22 | required = list(), 23 | additionalProperties = FALSE 24 | ) 25 | ) 26 | }) 27 | 28 | # tool_annotations() ------------------------------------------------------- 29 | 30 | test_that("tool_annotations(): NULL values are stripped", { 31 | expect_equal(tool_annotations(), set_names(list())) 32 | }) 33 | 34 | test_that("tool_annotations(): checks its inputs", { 35 | expect_snapshot(error = TRUE, { 36 | tool_annotations(title = list("Something unexpected")) 37 | tool_annotations(read_only_hint = "yes") 38 | tool_annotations(open_world_hint = "yes") 39 | tool_annotations(idempotent_hint = "no") 40 | tool_annotations(destructive_hint = "no") 41 | }) 42 | }) 43 | 44 | test_that("tool_annotations(): allows additional properties", { 45 | expect_equal( 46 | tool_annotations(description = "foo"), 47 | list(description = "foo") 48 | ) 49 | }) 50 | 51 | test_that("tool() allows annotations", { 52 | annotations <- tool_annotations(title = "My Tool", read_only_hint = TRUE) 53 | 54 | # fmt: skip 55 | expect_equal( 56 | tool(function() { }, "My tool", .annotations = annotations)@annotations, 57 | annotations 58 | ) 59 | }) 60 | -------------------------------------------------------------------------------- /tests/testthat/test-turns.R: -------------------------------------------------------------------------------- 1 | test_that("system prompt is applied correctly", { 2 | sys_prompt <- "foo" 3 | sys_msg <- Turn("system", sys_prompt) 4 | user_msg <- Turn("user", "bar") 5 | 6 | expect_equal(normalize_turns(), list()) 7 | expect_equal(normalize_turns(list(user_msg)), list(user_msg)) 8 | expect_equal(normalize_turns(list(sys_msg)), list(sys_msg)) 9 | 10 | expect_equal(normalize_turns(list(), sys_prompt), list(sys_msg)) 11 | expect_equal( 12 | normalize_turns(list(user_msg), sys_prompt), 13 | list(sys_msg, user_msg) 14 | ) 15 | expect_equal( 16 | normalize_turns(list(sys_msg, user_msg), sys_prompt), 17 | list(sys_msg, user_msg) 18 | ) 19 | }) 20 | 21 | test_that("normalize_turns throws useful errors", { 22 | sys_prompt <- "foo" 23 | sys_msg <- Turn("system", "foo") 24 | user_msg <- Turn("user", "bar") 25 | 26 | expect_snapshot(error = TRUE, { 27 | normalize_turns(1) 28 | normalize_turns(list(1)) 29 | normalize_turns(list(sys_msg, user_msg), 1) 30 | normalize_turns(list(sys_msg, user_msg), "foo2") 31 | }) 32 | }) 33 | 34 | 35 | test_that("as_user_turn gives useful errors", { 36 | expect_snapshot(error = TRUE, { 37 | as_user_turn(list()) 38 | as_user_turn(list(x = 1)) 39 | as_user_turn(1) 40 | }) 41 | }) 42 | 43 | test_that("as_user_turns gives useful errors", { 44 | x <- list(list(1)) 45 | expect_snapshot(error = TRUE, { 46 | as_user_turns(1) 47 | as_user_turns(x) 48 | }) 49 | }) 50 | 51 | test_that("can extract text easily", { 52 | turn <- Turn( 53 | "assistant", 54 | list( 55 | ContentText("ABC"), 56 | ContentImage(), 57 | ContentText("DEF") 58 | ) 59 | ) 60 | expect_equal(turn@text, "ABCDEF") 61 | }) 62 | 63 | test_that("turns have a reasonable print method", { 64 | expect_snapshot(Turn("user", "hello")) 65 | }) 66 | -------------------------------------------------------------------------------- /tests/testthat/test-utils-S7.R: -------------------------------------------------------------------------------- 1 | test_that("prop_whole_number validates inputs", { 2 | check_prop <- function(...) { 3 | new_class( 4 | "class", 5 | properties = list(prop = prop_number_whole(...)), 6 | package = NULL 7 | ) 8 | } 9 | expect_snapshot(error = TRUE, { 10 | check_prop()("x") 11 | check_prop()(c(1:2)) 12 | check_prop()(1.5) 13 | check_prop(min = 1)(0) 14 | check_prop(max = -1)(0) 15 | }) 16 | }) 17 | 18 | test_that("redacted values aren't saved to disk", { 19 | Test <- new_class("Test", properties = list(prop_redacted("redacted"))) 20 | 21 | # Can get and set redacted values 22 | test <- Test(redacted = "secret") 23 | expect_equal(test@redacted, "secret") 24 | test@redacted <- "new secret" 25 | expect_equal(test@redacted, "new secret") 26 | 27 | # But can't save it to disk 28 | path <- withr::local_tempfile() 29 | saveRDS(test, path) 30 | test <- readRDS(path) 31 | expect_equal(test@redacted, NULL) 32 | }) 33 | 34 | test_that("redacted values are instance specific", { 35 | # Reassure myself about the semantics of the weakrefs: even though the 36 | # key is shared across all instances of the same class, we are still creating 37 | # individual weakrefs for each instance. 38 | 39 | Test <- new_class("Test", properties = list(prop_redacted("redacted"))) 40 | test1 <- Test(redacted = "secret1") 41 | test2 <- Test(redacted = "secret2") 42 | expect_equal(test1@redacted, "secret1") 43 | expect_equal(test2@redacted, "secret2") 44 | 45 | test1@redacted <- "secret1a" 46 | test2@redacted <- "secret2a" 47 | expect_equal(test1@redacted, "secret1a") 48 | expect_equal(test2@redacted, "secret2a") 49 | }) 50 | -------------------------------------------------------------------------------- /tests/testthat/test-utils-callbacks.R: -------------------------------------------------------------------------------- 1 | test_that("CallbackManager catches argument mismatches", { 2 | callbacks <- CallbackManager$new(args = "data") 3 | 4 | expect_snapshot(error = TRUE, { 5 | callbacks$add("foo") 6 | callbacks$add(function(foo) NULL) 7 | callbacks$add(function(x, y) x + y) 8 | }) 9 | 10 | expect_silent(callbacks$add(function(data) data)) 11 | expect_silent(callbacks$invoke(data = data)) 12 | expect_silent(callbacks$invoke(1)) 13 | 14 | # Callbacks with invalid args throw standard R error 15 | expect_snapshot(error = TRUE, { 16 | callbacks$invoke() 17 | callbacks$invoke(1, 2) 18 | }) 19 | }) 20 | 21 | test_that("CallbackManager invokes callbacks in LIFO order", { 22 | callbacks <- CallbackManager$new() 23 | res1 <- NULL 24 | res2 <- NULL 25 | 26 | cb1 <- callbacks$add(function(value) { 27 | res1 <<- list(value = value, time = Sys.time()) 28 | }) 29 | 30 | cb2 <- callbacks$add(function(...) { 31 | res <- list(time = Sys.time()) 32 | res["value"] <- list(...) 33 | res2 <<- res 34 | }) 35 | 36 | expect_equal(callbacks$count(), 2) 37 | 38 | # Callbacks don't return a value 39 | expect_null(callbacks$invoke(list(x = 1, y = 2))) 40 | 41 | # Callbacks receive expected arguments 42 | expect_equal(res1$value, list(x = 1, y = 2)) 43 | expect_equal(res2$value, list(x = 1, y = 2)) 44 | # Callbacks are invoked in reverse order 45 | expect_true(res1$time > res2$time) 46 | }) 47 | 48 | test_that("$add() returns a function to remove the callback", { 49 | callbacks <- CallbackManager$new() 50 | res1 <- NULL 51 | res2 <- NULL 52 | 53 | cb1 <- callbacks$add(function() { 54 | res1 <<- Sys.time() 55 | }) 56 | cb2 <- callbacks$add(function() { 57 | res2 <<- Sys.time() 58 | }) 59 | 60 | expect_equal(callbacks$count(), 2) 61 | callbacks$invoke() 62 | 63 | # Unregistering a callback 64 | res1_first <- res1 65 | res2_first <- res2 66 | cb1() 67 | expect_equal(callbacks$count(), 1) 68 | callbacks$invoke() 69 | expect_equal(res1, res1_first) # first callback result hasn't changed 70 | expect_true(res2 > res2_first) # second callback was evaluated 71 | 72 | # Unregistering callbacks are idempotent 73 | cb_list <- callbacks$get_callbacks() 74 | expect_null(cb_list[["1"]]) 75 | cb1() 76 | # Callback list hasn't changed 77 | expect_equal(callbacks$get_callbacks(), cb_list) 78 | }) 79 | 80 | test_that("$clear() clears all callbacks", { 81 | callbacks <- CallbackManager$new() 82 | res <- c() 83 | callbacks$add(function() res <<- c(res, 1)) 84 | callbacks$add(function() res <<- c(res, 2)) 85 | expect_equal(callbacks$count(), 2) 86 | 87 | callbacks$clear() 88 | expect_equal(callbacks$count(), 0) 89 | expect_equal(callbacks$get_callbacks(), list()) 90 | 91 | # Invoking without registered callbacks means nothing happens 92 | expect_null(callbacks$invoke()) 93 | expect_equal(res, c()) # nothing happened 94 | }) 95 | -------------------------------------------------------------------------------- /tests/testthat/test-utils.R: -------------------------------------------------------------------------------- 1 | test_that("finds key if set", { 2 | withr::local_envvar(FOO = "abc123") 3 | expect_true(key_exists("FOO")) 4 | expect_equal(key_get("FOO"), "abc123") 5 | }) 6 | 7 | 8 | test_that("informative error if no key", { 9 | withr::local_envvar(FOO = NULL, TESTTHAT = "false") 10 | expect_false(key_exists("FOO")) 11 | expect_snapshot(key_get("FOO"), error = TRUE) 12 | }) 13 | 14 | test_that("detects whitespace", { 15 | expect_true(is_whitespace("\n\n\n \t")) 16 | expect_true(is_whitespace("")) 17 | 18 | expect_false(is_whitespace("a")) 19 | expect_false(is_whitespace(".")) 20 | }) 21 | 22 | test_that('echo="output" replaces echo="text"', { 23 | expect_snapshot( 24 | expect_equal(check_echo("text"), "output") 25 | ) 26 | }) 27 | -------------------------------------------------------------------------------- /tests/testthat/tools-def.R: -------------------------------------------------------------------------------- 1 | # fmt: skip file 2 | 3 | #' A function for foo-ing three numbers. 4 | #' 5 | #' @param x The first param 6 | #' @param y The second param 7 | #' @param z Take a guess 8 | #' @returns The result of x %foo% y %foo% z. 9 | has_roxygen_comments <- function(x, y, z = pi - 3.14) { 10 | super_secret_code() 11 | } 12 | 13 | 14 | #' A function for foo-ing three numbers. 15 | #' 16 | #' @param x The first param 17 | #' @param y The second param 18 | #' @param z Take a guess 19 | #' @returns The result of x %foo% y %foo% z. 20 | indented_comments <- function(x, y, z = pi - 3.14) { 21 | super_secret_code() 22 | } 23 | 24 | no_roxygen_comments <- function(i, j, k = pi - 3.14) { 25 | super_secret_code() 26 | } 27 | 28 | no_srcfile <- eval(quote( 29 | #' A function for foo-ing three numbers. 30 | function(a, b, c = pi - 3.14) { 31 | super_secret_code() 32 | } 33 | )) 34 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | 4 | /.quarto/ 5 | -------------------------------------------------------------------------------- /vignettes/congressional-assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/congressional-assets.png -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/__packages: -------------------------------------------------------------------------------- 1 | ellmer 2 | -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-basic_23c2af49b209e7f837cf1d5796fe658a.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-explicit_7a476b83a4bd49d76fd895c47b6e94ce.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-new-feature_50952fd1cf498c080ef900a6b6566fea.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r-minimal_5e47b0692ff90e220d5873f80a8b88b1.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-r_a315a31de513b9570329f8d6ba458bb4.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-styles_e2dd9503e443bb18fb347d207d80d38d.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/code-teacher_543ffae480ea561caf9310e4557e122f.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-examples_4417be86e888119b66334c9d09d2e349.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-loose_0ab119dbf3f2ec9764f721b9e9ce7292.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-structured_9d96a9eafa0565bc04fc1679b0291b91.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/data-unstructured-input_f04e9f6842ac6924f558b59eb7c6ea0a.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/unnamed-chunk-13_b296e03e021fe4374196cb7de5637bc5.rdx -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.RData -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.rdb -------------------------------------------------------------------------------- /vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/prompt-design_cache/html/usage_7450dd5600850638f2f463ce382f71a9.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/__packages: -------------------------------------------------------------------------------- 1 | ellmer 2 | -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-image_5394a0a58034be7a3ff41e5a7a40e01b.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/basics-text_f9f3c2d2259df508e0ada9e224adbe7f.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-classification_345ace7803d5add79763d1565d129266.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-image_ba40428e2ad729994ce734eb071a80fe.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-named-entity_36ebe59c4ec94cb4673b121a3724ff13.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-sentiment_ef29c06557ae94b99008e5978fe525dd.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-summarisation_b4eeeab772279f10541991a197bc8c24.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/examples-unknown-keys_42a9958ad941f07bc7ec26edfd649a0d.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/setup_708b46f5a397cb58f728f88aa9935154.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-optional_1ddc90c528cd0b05153da187c33083f0.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/type-required_2b6b688f55ee2d8da6a62dfac030c328.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/unnamed-chunk-12_c21bfc439ea4b169c82ebc07c6a39dc0.rdx -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.RData -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.rdb -------------------------------------------------------------------------------- /vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverse/ellmer/75030a69f0045d5d01bd8e246c9e8dc43acae2db/vignettes/structured-data_cache/html/usage_a390555c32af75ba9192f9addd5b44d1.rdx --------------------------------------------------------------------------------