├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── actions │ └── setup-poetry-env │ │ └── action.yml ├── scripts │ └── get_toolkits.sh └── workflows │ ├── check-toolkits.yml │ ├── check-unauthorized-toolkit-updates.yml │ ├── main.yml │ ├── porter_app_worker_4828.yml │ ├── publish-langchain.yml │ ├── publish-toolkit.yml │ ├── release-containers.yml │ ├── test-langchain.yml │ └── test-toolkits.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc.toml ├── .ruff.toml ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── arcade ├── README.md ├── arcade │ ├── __init__.py │ ├── cli │ │ ├── __init__.py │ │ ├── authn.py │ │ ├── constants.py │ │ ├── deployment.py │ │ ├── display.py │ │ ├── launcher.py │ │ ├── main.py │ │ ├── new.py │ │ ├── serve.py │ │ ├── show.py │ │ ├── utils.py │ │ └── worker.py │ ├── core │ │ ├── annotations.py │ │ ├── auth.py │ │ ├── catalog.py │ │ ├── config.py │ │ ├── config_model.py │ │ ├── errors.py │ │ ├── executor.py │ │ ├── output.py │ │ ├── parse.py │ │ ├── schema.py │ │ ├── telemetry.py │ │ ├── toolkit.py │ │ ├── utils.py │ │ └── version.py │ ├── py.typed │ ├── sdk │ │ ├── __init__.py │ │ ├── annotations │ │ │ └── __init__.py │ │ ├── auth │ │ │ └── __init__.py │ │ ├── errors.py │ │ ├── eval │ │ │ ├── __init__.py │ │ │ ├── critic.py │ │ │ └── eval.py │ │ └── tool.py │ ├── templates │ │ └── {{ toolkit_name }} │ │ │ ├── .editorconfig │ │ │ ├── .github │ │ │ ├── actions │ │ │ │ └── setup-poetry-env │ │ │ │ │ └── action.yml │ │ │ └── workflows │ │ │ │ ├── main.yml │ │ │ │ └── publish-to-pypi.yml │ │ │ ├── .gitignore │ │ │ ├── .pre-commit-config.yaml │ │ │ ├── .prettierignore │ │ │ ├── .prettierrc.toml │ │ │ ├── .ruff.toml │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── codecov.yaml │ │ │ ├── evals │ │ │ └── eval_{{ toolkit_name }}.py │ │ │ ├── pyproject.toml │ │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_{{ toolkit_name }}.py │ │ │ ├── tox.ini │ │ │ └── {{ package_name }} │ │ │ ├── __init__.py │ │ │ └── tools │ │ │ ├── __init__.py │ │ │ └── hello.py │ └── worker │ │ ├── __init__.py │ │ ├── core │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── base.py │ │ ├── common.py │ │ └── components.py │ │ ├── fastapi │ │ ├── __init__.py │ │ ├── auth.py │ │ └── worker.py │ │ ├── mcp │ │ ├── __init__.py │ │ ├── convert.py │ │ ├── logging.py │ │ ├── message_processor.py │ │ ├── server.py │ │ ├── stdio.py │ │ └── types.py │ │ └── utils.py ├── codecov.yaml ├── poetry.toml ├── pyproject.toml ├── run_cli.py ├── tests │ ├── __init__.py │ ├── cli │ │ ├── test_dashboard.py │ │ ├── test_show.py │ │ └── test_utils.py │ ├── core │ │ ├── test_catalog.py │ │ ├── test_executor.py │ │ ├── test_output.py │ │ ├── test_parse.py │ │ ├── test_schema.py │ │ ├── test_telemetry.py │ │ └── utils │ │ │ ├── test_casing.py │ │ │ ├── test_is_strict_optional.py │ │ │ └── test_is_union.py │ ├── deployment │ │ ├── test_config.py │ │ └── test_files │ │ │ ├── env.secret.worker.toml │ │ │ ├── full.worker.toml │ │ │ ├── invalid.fields.worker.toml │ │ │ ├── invalid.localfile.worker.toml │ │ │ ├── invalid.secret.worker.toml │ │ │ ├── invalid_toolkit │ │ │ └── invalid_main.py │ │ │ └── mock_toolkit │ │ │ ├── mock_main.py │ │ │ └── pyproject.toml │ ├── mcp │ │ ├── test_convert.py │ │ ├── test_message_processor.py │ │ ├── test_server.py │ │ └── test_stdio.py │ ├── sdk │ │ ├── test_eval.py │ │ ├── test_eval_critic.py │ │ └── test_tool_decorator.py │ ├── tool │ │ ├── test_create_tool_definition.py │ │ ├── test_create_tool_definition_errors.py │ │ ├── test_create_tool_definition_new.py │ │ ├── test_create_tool_definition_pydantic.py │ │ ├── test_create_tool_definition_pydantic_errors.py │ │ └── test_fully_qualified_tool_name.py │ └── worker │ │ ├── test_worker_base.py │ │ └── test_worker_fastapi.py └── tox.ini ├── contrib ├── crewai │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── crewai_arcade │ │ ├── __init__.py │ │ ├── _utilities.py │ │ ├── manager.py │ │ ├── py.typed │ │ └── structured.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_manager.py │ │ └── test_structured_tool.py └── langchain │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── langchain_arcade │ ├── __init__.py │ ├── _utilities.py │ ├── manager.py │ └── py.typed │ ├── pyproject.toml │ ├── tests │ ├── conftest.py │ └── test_manager.py │ └── tox.ini ├── cspell.config.yaml ├── docker ├── Dockerfile ├── Makefile ├── README.md ├── docker-compose.yml ├── docker.engine.yaml ├── env.example ├── nginx.conf ├── start.sh └── toolkits.txt ├── examples ├── .gitignore ├── ai-sdk │ ├── .env.example │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── generateText.js │ ├── index.js │ ├── package.json │ └── pnpm-lock.yaml ├── call_a_tool_directly.py ├── call_a_tool_directly_with_auth.py ├── call_a_tool_with_llm.py ├── crewai │ ├── crewai_with_arcade_tool.py │ ├── requirements.txt │ └── simple_crewai_with_arcade_tool.py ├── get_auth_token.py ├── langchain-ts │ ├── .env.example │ ├── .gitignore │ ├── LICENSE │ ├── langchain-tool-arcade-auth.ts │ ├── langgraph-arcade-minimal.ts │ ├── langgraph-with-user-auth.ts │ ├── langgraph.json │ ├── package-lock.json │ ├── package.json │ ├── pnpm-lock.yaml │ └── tsconfig.json ├── langchain │ ├── langchain_tool_arcade_auth.py │ ├── langgraph_arcade_minimal.py │ ├── langgraph_with_user_auth.py │ ├── requirements.txt │ └── studio │ │ ├── README.md │ │ ├── configuration.py │ │ ├── env.example │ │ ├── graph.py │ │ ├── langgraph.json │ │ └── requirements.txt ├── langgraph-ts │ ├── .gitignore │ ├── README.md │ ├── langgraph.json │ ├── package.json │ ├── src │ │ ├── configuration.ts │ │ ├── graph.ts │ │ └── prompts.ts │ └── studio.png ├── mastra │ ├── .env.example │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ │ └── mastra │ │ │ ├── agents │ │ │ └── google.ts │ │ │ └── index.ts │ └── tsconfig.json ├── mcp │ ├── claude.json │ └── run_stdio.py └── serving-tools │ ├── docker │ ├── Dockerfile │ ├── README.md │ └── toolkits.txt │ └── modal │ ├── README.md │ └── run-arcade-worker.py ├── schemas └── preview │ ├── invoke_tool_request.schema.jsonc │ ├── invoke_tool_response.schema.jsonc │ └── tool_definition.schema.jsonc ├── toolkits ├── TOOLKIT_RELEASE_MANAGERS.txt ├── asana │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_asana │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── decorators.py │ │ ├── exceptions.py │ │ ├── models.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── projects.py │ │ │ ├── tags.py │ │ │ ├── tasks.py │ │ │ ├── teams.py │ │ │ ├── users.py │ │ │ └── workspaces.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_create_task.py │ │ ├── eval_projects.py │ │ ├── eval_tags.py │ │ ├── eval_tasks.py │ │ ├── eval_teams.py │ │ ├── eval_users.py │ │ └── eval_workspaces.py │ ├── pyproject.toml │ └── tests │ │ └── test_utils.py ├── code_sandbox │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_code_sandbox │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── e2b.py │ │ │ └── models.py │ ├── evals │ │ └── eval_e2b.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ └── test_e2b.py ├── confluence │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_confluence │ │ ├── __init__.py │ │ ├── client.py │ │ ├── enums.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── attachment.py │ │ │ ├── page.py │ │ │ ├── search.py │ │ │ └── space.py │ │ └── utils.py │ ├── evals │ │ ├── conversations.py │ │ ├── critics.py │ │ ├── eval_page.py │ │ ├── eval_search.py │ │ └── eval_space.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_client_v1.py │ │ ├── test_client_v2.py │ │ └── test_utils.py ├── dropbox │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_dropbox │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── critics.py │ │ ├── exceptions.py │ │ ├── tools │ │ │ ├── browse.py │ │ │ └── files.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_download_file.py │ │ ├── eval_list_items.py │ │ └── eval_search.py │ ├── pyproject.toml │ └── tests │ │ ├── test_download_file.py │ │ ├── test_list_items.py │ │ └── test_search_files.py ├── github │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_github │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── activity.py │ │ │ ├── constants.py │ │ │ ├── issues.py │ │ │ ├── models.py │ │ │ ├── pull_requests.py │ │ │ ├── repositories.py │ │ │ └── utils.py │ ├── evals │ │ ├── eval_github_activity.py │ │ ├── eval_github_issues.py │ │ ├── eval_github_pull_requests.py │ │ └── eval_github_repositories.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_activity.py │ │ ├── test_issues.py │ │ ├── test_pull_requests.py │ │ └── test_repositories.py ├── google │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_google │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── critics.py │ │ ├── doc_to_html.py │ │ ├── doc_to_markdown.py │ │ ├── enums.py │ │ ├── exceptions.py │ │ ├── models.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── calendar.py │ │ │ ├── contacts.py │ │ │ ├── docs.py │ │ │ ├── drive.py │ │ │ ├── file_picker.py │ │ │ ├── gmail.py │ │ │ └── sheets.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_calendar_free_slots.py │ │ ├── eval_file_picker.py │ │ ├── eval_google_calendar.py │ │ ├── eval_google_contacts.py │ │ ├── eval_google_docs.py │ │ ├── eval_google_drive.py │ │ ├── eval_google_gmail.py │ │ └── eval_google_sheets.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_calendar.py │ │ ├── test_contacts.py │ │ ├── test_doc_to_markdown.py │ │ ├── test_docs.py │ │ ├── test_drive.py │ │ ├── test_file_picker.py │ │ ├── test_gmail.py │ │ ├── test_sheets_models.py │ │ └── test_sheets_utils.py ├── hubspot │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_hubspot │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── custom_critics.py │ │ ├── enums.py │ │ ├── exceptions.py │ │ ├── models.py │ │ ├── properties.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ └── crm │ │ │ │ ├── companies.py │ │ │ │ └── contacts.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_crm_companies.py │ │ └── eval_crm_contacts.py │ ├── pyproject.toml │ └── tests │ │ └── test_hubspot_client.py ├── linkedin │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_linkedin │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── constants.py │ │ │ ├── share.py │ │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ └── eval_linkedin.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ └── test_share.py ├── math │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_math │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── arithmetic.py │ │ │ ├── exponents.py │ │ │ ├── miscellaneous.py │ │ │ ├── random.py │ │ │ ├── rational.py │ │ │ ├── rounding.py │ │ │ ├── statistics.py │ │ │ └── trigonometry.py │ ├── evals │ │ └── eval_math_tools.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_arithmetic.py │ │ ├── test_exponents.py │ │ ├── test_miscellaneous.py │ │ ├── test_rational.py │ │ ├── test_rounding.py │ │ ├── test_statistics.py │ │ └── test_trigonometry.py ├── microsoft │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_microsoft │ │ ├── __init__.py │ │ ├── client.py │ │ ├── outlook_calendar │ │ │ ├── __init__.py │ │ │ ├── _utils.py │ │ │ ├── constants.py │ │ │ ├── models.py │ │ │ └── tools │ │ │ │ ├── __init__.py │ │ │ │ ├── create_event.py │ │ │ │ ├── get_event.py │ │ │ │ └── list_events_in_time_range.py │ │ └── outlook_mail │ │ │ ├── __init__.py │ │ │ ├── _utils.py │ │ │ ├── constants.py │ │ │ ├── enums.py │ │ │ ├── message.py │ │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── read.py │ │ │ ├── send.py │ │ │ └── write.py │ ├── evals │ │ ├── outlook_calendar │ │ │ ├── additional_messages.py │ │ │ ├── eval_create_event.py │ │ │ ├── eval_get_event.py │ │ │ └── eval_list_events_in_time_range.py │ │ └── outlook_mail │ │ │ ├── additional_messages.py │ │ │ ├── eval_read.py │ │ │ ├── eval_send.py │ │ │ └── eval_write.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── outlook_calendar │ │ ├── __init__.py │ │ ├── test_models.py │ │ └── test_utils.py │ │ └── outlook_mail │ │ ├── __init__.py │ │ ├── test_message.py │ │ ├── test_recipient.py │ │ └── test_utils.py ├── notion │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_notion_toolkit │ │ ├── __init__.py │ │ ├── block_to_markdown_converter.py │ │ ├── constants.py │ │ ├── enums.py │ │ ├── markdown_to_block_converter.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── pages.py │ │ │ └── search.py │ │ ├── types.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_notion_pages.py │ │ └── eval_notion_search.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_block_to_markdown_converter.py │ │ ├── test_tools_pages.py │ │ ├── test_tools_search.py │ │ └── test_utils.py ├── reddit │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_reddit │ │ ├── __init__.py │ │ ├── client.py │ │ ├── enums.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── read.py │ │ │ └── submit.py │ │ └── utils.py │ ├── evals │ │ ├── additional_messages.py │ │ ├── critics.py │ │ ├── eval_reddit_read.py │ │ └── eval_reddit_submit.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ └── test_utils.py ├── salesforce │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_salesforce │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── enums.py │ │ ├── exceptions.py │ │ ├── models.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ └── crm │ │ │ │ ├── account.py │ │ │ │ └── contact.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_create_contact.py │ │ └── eval_get_account_data.py │ ├── pyproject.toml │ └── tests │ │ ├── test_create_contact.py │ │ ├── test_get_account_data.py │ │ └── test_salesforce_client.py ├── search │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_search │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── enums.py │ │ ├── exceptions.py │ │ ├── google_data.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── google_finance.py │ │ │ ├── google_flights.py │ │ │ ├── google_hotels.py │ │ │ ├── google_jobs.py │ │ │ ├── google_maps.py │ │ │ ├── google_news.py │ │ │ ├── google_search.py │ │ │ ├── google_shopping.py │ │ │ ├── walmart.py │ │ │ └── youtube.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_google_jobs.py │ │ ├── eval_google_maps_directions.py │ │ └── eval_google_search.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_google_jobs.py │ │ ├── test_google_maps_directions.py │ │ ├── test_google_search.py │ │ └── test_utils.py ├── slack │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_slack │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── critics.py │ │ ├── custom_types.py │ │ ├── exceptions.py │ │ ├── models.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── chat.py │ │ │ └── users.py │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_chat.py │ │ └── eval_users.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_chat.py │ │ ├── test_users.py │ │ └── test_utils.py ├── spotify │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_spotify │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── constants.py │ │ │ ├── models.py │ │ │ ├── player.py │ │ │ ├── search.py │ │ │ ├── tracks.py │ │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ ├── eval_player.py │ │ ├── eval_search.py │ │ └── eval_tracks.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_player.py │ │ ├── test_search.py │ │ ├── test_tracks.py │ │ └── test_utils.py ├── stripe │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── _generate.py │ ├── arcade_stripe │ │ ├── __init__.py │ │ ├── evals │ │ │ └── eval_stripe.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ └── stripe.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ └── test_stripe.py ├── web │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_web │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── firecrawl.py │ │ │ └── models.py │ ├── evals │ │ └── eval_firecrawl.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ └── test_firecrawl.py ├── x │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_x │ │ ├── __init__.py │ │ └── tools │ │ │ ├── __init__.py │ │ │ ├── constants.py │ │ │ ├── tweets.py │ │ │ ├── users.py │ │ │ └── utils.py │ ├── conftest.py │ ├── evals │ │ └── eval_x_tools.py │ ├── pyproject.toml │ └── tests │ │ ├── __init__.py │ │ ├── test_tweets.py │ │ └── test_users.py └── zoom │ ├── .pre-commit-config.yaml │ ├── .ruff.toml │ ├── LICENSE │ ├── Makefile │ ├── arcade_zoom │ ├── __init__.py │ └── tools │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── meetings.py │ │ └── utils.py │ ├── pyproject.toml │ └── tests │ ├── __init__.py │ └── test_meetings.py └── worker.toml /.editorconfig: -------------------------------------------------------------------------------- 1 | # Stop the editor from looking for .editorconfig files in the parent directories 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | end_of_line = lf 8 | indent_style = space 9 | indent_size = 4 10 | max_line_length = 100 # This is also set in .ruff.toml for ruff 11 | 12 | [*.{json,jsonc,yml,yaml}] 13 | indent_style = space 14 | indent_size = 2 # This is also set in .prettierrc.toml 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: "[Bug] " 5 | labels: "type: bug" 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the Bug** 11 | Provide a clear and concise description of what the bug is. 12 | 13 | **How to Reproduce** 14 | Steps to reproduce the bug: 15 | 1. Configure the environment or settings with... 16 | 2. Run the command... 17 | 3. Observe the error or unexpected output at... 18 | 4. Log output/error message 19 | 20 | **Expected Behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain the issue you are experiencing. 25 | 26 | **Environment (please complete the following information):** 27 | - OS: [e.g. macOS, Linux, Windows] 28 | - Version/commit of Arcade AI: [e.g. 0.1.0, or the commit hash] 29 | 30 | **Logs** 31 | If applicable, include detailed logs to help understand the problem. 32 | 33 | **Additional Context** 34 | Add any other context about the problem here 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Feature suggestion or idea 4 | title: "[Feature] " 5 | labels: "type: feature" 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description** 11 | What is the point of the feature you would like to see implemented? Who/what will benefit from it? 12 | 13 | **Proposed Feature** 14 | Provide a clear and concise description of the feature you would like implemented. 15 | 16 | **Alternatives Considered** 17 | Discuss any alternative solutions or features you've considered. Why were these alternatives not suitable? 18 | 19 | **Implementation Strategy** 20 | If you have ideas on how the feature could be implemented, share them here. This could include technical details, screenshots, mockups, etc. 21 | 22 | **Use Case** 23 | Explain how this feature would be used and what benefits it would bring. Include specific examples to illustrate how this would improve functionality or user experience. 24 | 25 | **Additional Context** 26 | Add any other context that would help the reader understand the feature request. 27 | -------------------------------------------------------------------------------- /.github/actions/setup-poetry-env/action.yml: -------------------------------------------------------------------------------- 1 | name: "setup-poetry-env" 2 | description: "Composite action to setup the Python and poetry environment." 3 | 4 | inputs: 5 | python-version: 6 | required: false 7 | description: "The python version to use" 8 | default: "3.11" 9 | 10 | runs: 11 | using: "composite" 12 | steps: 13 | - name: Set up python 14 | uses: actions/setup-python@v5 15 | with: 16 | python-version: ${{ inputs.python-version }} 17 | 18 | - name: Install Poetry 19 | uses: snok/install-poetry@v1 20 | with: 21 | version: 1.8.5 22 | virtualenvs-in-project: true 23 | 24 | - name: Generate poetry.lock 25 | run: cd arcade && poetry lock --no-update 26 | shell: bash 27 | 28 | - name: Load cached venv 29 | id: cached-poetry-dependencies 30 | uses: actions/cache@v4 31 | with: 32 | path: .venv 33 | key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('arcade/poetry.lock') }} 34 | 35 | - name: Install dependencies 36 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 37 | run: cd arcade && poetry install --no-interaction --all-extras 38 | shell: bash 39 | -------------------------------------------------------------------------------- /.github/scripts/get_toolkits.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get all directories and format as JSON array 4 | echo -n '[' 5 | ls -d toolkits/*/ | cut -d'/' -f2 | sort -u | awk '{ 6 | if (NR==1) { 7 | printf "\"%s\"", $0 8 | } else { 9 | printf ", \"%s\"", $0 10 | } 11 | }' 12 | echo ']' 13 | -------------------------------------------------------------------------------- /.github/workflows/porter_app_worker_4828.yml: -------------------------------------------------------------------------------- 1 | "on": 2 | push: 3 | branches: 4 | - main 5 | name: Deploy to worker 6 | jobs: 7 | porter-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | - name: Set Github tag 13 | id: vars 14 | run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT 15 | - name: Setup porter 16 | uses: porter-dev/setup-porter@v0.1.0 17 | - name: Deploy stack 18 | timeout-minutes: 30 19 | run: exec porter apply 20 | env: 21 | PORTER_APP_NAME: worker 22 | PORTER_CLUSTER: "4828" 23 | PORTER_DEPLOYMENT_TARGET_ID: 9da4edac-88d4-4409-afe7-e885dd64d6f0 24 | PORTER_HOST: https://dashboard.porter.run 25 | PORTER_PR_NUMBER: ${{ github.event.number }} 26 | PORTER_PROJECT: "15823" 27 | PORTER_REPO_NAME: ${{ github.event.repository.name }} 28 | PORTER_TAG: ${{ steps.vars.outputs.sha_short }} 29 | PORTER_TOKEN: ${{ secrets.PORTER_APP_15823_4828 }} 30 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: "v4.4.0" 4 | hooks: 5 | - id: check-case-conflict 6 | - id: check-merge-conflict 7 | - id: check-toml 8 | - id: check-yaml 9 | exclude: ".*/templates/.*" 10 | - id: end-of-file-fixer 11 | exclude: ".*/templates/.*" 12 | - id: trailing-whitespace 13 | 14 | - repo: https://github.com/astral-sh/ruff-pre-commit 15 | rev: v0.6.7 16 | hooks: 17 | - id: ruff 18 | args: [--fix] 19 | exclude: ".*/templates/.*" 20 | - id: ruff-format 21 | exclude: ".*/templates/.*" 22 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore Python files for Prettier 2 | *.py 3 | -------------------------------------------------------------------------------- /.prettierrc.toml: -------------------------------------------------------------------------------- 1 | # See https://prettier.io/docs/en/configuration 2 | 3 | # Note: This prettier config is only for the non-python files in this repo. 4 | # Python files are formatted with ruff and ignored in .prettierignore 5 | 6 | trailingComma = "es5" 7 | tabWidth = 4 8 | semi = false 9 | singleQuote = false 10 | 11 | [[overrides]] 12 | files = [ "*.json", "*.jsonc", "*.yml", "*.yaml" ] 13 | 14 | [overrides.options] 15 | tabWidth = 2 16 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py39" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | # TODO work to remove these 40 | ignore = [ 41 | # LineTooLong 42 | "E501", 43 | # DoNotAssignLambda 44 | "E731", 45 | # raise from (cli specific) 46 | "B904", # Previously "TRY200" 47 | # Depends function in arg string 48 | "B008", 49 | # raise from (cli specific) 50 | "B904", 51 | # long message exceptions 52 | "TRY003", 53 | # subprocess.Popen 54 | "S603", 55 | ] 56 | 57 | [lint.per-file-ignores] 58 | "**/tests/*" = ["S101"] 59 | 60 | [format] 61 | preview = true 62 | skip-magic-trailing-comma = false 63 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "charliermarsh.ruff", 4 | "esbenp.prettier-vscode", 5 | "streetsidesoftware.code-spell-checker", 6 | "editorconfig.editorconfig", 7 | "ms-python.vscode-pylance" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/__pycache__": true, 4 | "**/.mypy_cache": true, 5 | "**/.pytest_cache": true, 6 | "**/.ruff_cache": true 7 | }, 8 | "python.testing.unittestEnabled": false, 9 | "python.testing.pytestEnabled": true, 10 | "[python]": { 11 | "editor.formatOnSave": true, 12 | "editor.codeActionsOnSave": { 13 | "source.organizeImports": "explicit" 14 | }, 15 | "editor.defaultFormatter": "charliermarsh.ruff" 16 | }, 17 | "[jsonc]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode", 19 | "editor.formatOnSave": true 20 | }, 21 | "[json]": { 22 | "editor.defaultFormatter": "esbenp.prettier-vscode", 23 | "editor.formatOnSave": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /arcade/README.md: -------------------------------------------------------------------------------- 1 | # Arcade Python SDK and CLI 2 | 3 | [Arcade](https://arcade.dev?ref=pypi) provides developer-focused tooling and APIs designed to improve the capabilities of LLM applications and agents. 4 | 5 | By removing the complexity of connecting agentic applications with your users' data and services, Arcade enables developers to focus on building their agentic applications. 6 | 7 | To learn more, check out our 8 | - [Website](https://arcade.dev?ref=pypi) 9 | - [GitHub](https://github.com/ArcadeAI/arcade-ai) 10 | - [Documentation](https://docs.arcade.dev) 11 | - [Discord](https://discord.com/invite/GUZEMpEZ9p) 12 | - [X](https://x.com/TryArcade) 13 | - [LinkedIn](https://www.linkedin.com/company/arcade-ai) 14 | -------------------------------------------------------------------------------- /arcade/arcade/__init__.py: -------------------------------------------------------------------------------- 1 | from importlib.metadata import version as get_version 2 | 3 | __version__ = get_version("arcade-ai") 4 | -------------------------------------------------------------------------------- /arcade/arcade/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/cli/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/core/annotations.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | 4 | @dataclass(frozen=True) 5 | class Inferrable: 6 | """An annotation indicating that a parameter can be inferred by a model (default: True).""" 7 | 8 | value: bool = True 9 | -------------------------------------------------------------------------------- /arcade/arcade/core/config.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | from arcade.core.config_model import Config 4 | 5 | 6 | @lru_cache(maxsize=1) 7 | def get_config() -> Config: 8 | """ 9 | Get the Arcade configuration. 10 | 11 | This function is cached, so subsequent calls will return the same Config object 12 | without reloading from the file, unless the cache is cleared. 13 | 14 | remember to clear the cache if the configuration file is modified. 15 | use `get_config.cache_clear()` to clear the cache. 16 | 17 | Returns: 18 | Config: The Arcade configuration. 19 | """ 20 | return Config.load_from_file() 21 | 22 | 23 | config = get_config() 24 | -------------------------------------------------------------------------------- /arcade/arcade/core/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "0.1.0" 2 | -------------------------------------------------------------------------------- /arcade/arcade/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/py.typed -------------------------------------------------------------------------------- /arcade/arcade/sdk/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade.core.catalog import ToolCatalog 2 | from arcade.core.schema import ToolAuthorizationContext, ToolContext, ToolMetadataKey 3 | from arcade.core.toolkit import Toolkit 4 | 5 | from .tool import tool 6 | 7 | __all__ = [ 8 | "ToolAuthorizationContext", 9 | "ToolCatalog", 10 | "ToolContext", 11 | "ToolMetadataKey", 12 | "Toolkit", 13 | "tool", 14 | ] 15 | -------------------------------------------------------------------------------- /arcade/arcade/sdk/annotations/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade.core.annotations import Inferrable 2 | 3 | __all__ = ["Inferrable"] 4 | -------------------------------------------------------------------------------- /arcade/arcade/sdk/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade.core.auth import ( 2 | Asana, 3 | Atlassian, 4 | Discord, 5 | Dropbox, 6 | GitHub, 7 | Google, 8 | Hubspot, 9 | LinkedIn, 10 | Microsoft, 11 | Notion, 12 | OAuth2, 13 | Reddit, 14 | Slack, 15 | Spotify, 16 | ToolAuthorization, 17 | Twitch, 18 | X, 19 | Zoom, 20 | ) 21 | 22 | __all__ = [ 23 | "Asana", 24 | "Atlassian", 25 | "Discord", 26 | "Dropbox", 27 | "GitHub", 28 | "Google", 29 | "Hubspot", 30 | "LinkedIn", 31 | "Microsoft", 32 | "Notion", 33 | "OAuth2", 34 | "Reddit", 35 | "Slack", 36 | "Spotify", 37 | "ToolAuthorization", 38 | "Twitch", 39 | "X", 40 | "Zoom", 41 | ] 42 | -------------------------------------------------------------------------------- /arcade/arcade/sdk/errors.py: -------------------------------------------------------------------------------- 1 | from arcade.core.errors import RetryableToolError, ToolExecutionError, ToolRuntimeError 2 | 3 | __all__ = [ 4 | "SDKError", 5 | "WeightError", 6 | "ToolRuntimeError", 7 | "ToolExecutionError", 8 | "RetryableToolError", 9 | ] 10 | 11 | 12 | class SDKError(Exception): 13 | """Base class for all SDK errors.""" 14 | 15 | 16 | class WeightError(SDKError): 17 | """Raised when the critic weights do not abide by SDK weight constraints.""" 18 | -------------------------------------------------------------------------------- /arcade/arcade/sdk/eval/__init__.py: -------------------------------------------------------------------------------- 1 | from .critic import BinaryCritic, DatetimeCritic, NoneCritic, NumericCritic, SimilarityCritic 2 | from .eval import EvalRubric, EvalSuite, ExpectedToolCall, NamedExpectedToolCall, tool_eval 3 | 4 | __all__ = [ 5 | "BinaryCritic", 6 | "DatetimeCritic", 7 | "EvalRubric", 8 | "EvalSuite", 9 | "ExpectedToolCall", 10 | "NamedExpectedToolCall", 11 | "NoneCritic", 12 | "NumericCritic", 13 | "SimilarityCritic", 14 | "tool_eval", 15 | ] 16 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.editorconfig: -------------------------------------------------------------------------------- 1 | # Stop the editor from looking for .editorconfig files in the parent directories 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | end_of_line = lf 8 | indent_style = space 9 | indent_size = 4 10 | max_line_length = 100 # This is also set in .ruff.toml for ruff 11 | 12 | [*.{json,jsonc,yml,yaml}] 13 | indent_style = space 14 | indent_size = 2 # This is also set in .prettierrc.toml 15 | 16 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.github/actions/setup-poetry-env/action.yml: -------------------------------------------------------------------------------- 1 | name: "setup-poetry-env"{% raw %} 2 | description: "Composite action to setup the Python and poetry environment." 3 | 4 | inputs: 5 | python-version: 6 | required: false 7 | description: "The python version to use" 8 | default: "3.11" 9 | 10 | runs: 11 | using: "composite" 12 | steps: 13 | - name: Set up python 14 | uses: actions/setup-python@v5 15 | with: 16 | python-version: ${{ inputs.python-version }} 17 | 18 | - name: Install Poetry 19 | uses: snok/install-poetry@v1 20 | with: 21 | version: 1.8.5 22 | virtualenvs-in-project: true 23 | 24 | - name: Generate poetry.lock 25 | run: poetry lock --no-update 26 | shell: bash 27 | 28 | - name: Load cached venv 29 | id: cached-poetry-dependencies 30 | uses: actions/cache@v4 31 | with: 32 | path: .venv 33 | key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('poetry.lock') }} 34 | 35 | - name: Install dependencies 36 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 37 | run: poetry install --no-interaction --all-extras 38 | shell: bash 39 | {% endraw %} 40 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | pypi-publish: 9 | name: Publish to PyPi 10 | runs-on: ubuntu-latest 11 | environment: 12 | name: pypi 13 | url: https://pypi.org/project/{{ package_name }} 14 | permissions: 15 | id-token: write 16 | steps: 17 | - name: Check out 18 | uses: actions/checkout@v4 19 | 20 | - name: Set up python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.12' 24 | 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install build twine 29 | 30 | - name: Build package 31 | run: python -m build 32 | 33 | - name: Publish to PyPI 34 | env: 35 | TWINE_USERNAME: "__token__"{% raw %} 36 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 37 | run: twine upload dist/* 38 | {% endraw %} 39 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | 20 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore Python files for Prettier 2 | *.py 3 | 4 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.prettierrc.toml: -------------------------------------------------------------------------------- 1 | # See https://prettier.io/docs/en/configuration 2 | 3 | # Note: This prettier config is only for the non-python files in this repo. 4 | # Python files are formatted with ruff and ignored in .prettierignore 5 | 6 | trailingComma = "es5" 7 | tabWidth = 4 8 | semi = false 9 | singleQuote = false 10 | 11 | [[overrides]] 12 | files = [ "*.json", "*.jsonc", "*.yml", "*.yaml" ] 13 | 14 | [overrides.options] 15 | tabWidth = 2 16 | 17 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | 46 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) {{ creation_year }}, {{ toolkit_author_name }} 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 | 23 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/codecov.yaml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 70..100 3 | round: down 4 | precision: 1 5 | status: 6 | project: 7 | default: 8 | target: 90% 9 | threshold: 0.5% 10 | 11 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "{{ package_name }}" 3 | version = "0.0.1" 4 | description = "{{ toolkit_description }}" 5 | authors = ["{{ toolkit_author_name }} <{{ toolkit_author_email }}>"] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = "^1.0.5" 10 | 11 | [tool.poetry.dev-dependencies] 12 | pytest = "^8.3.0" 13 | pytest-cov = "^4.0.0" 14 | mypy = "^1.5.1" 15 | pre-commit = "^3.4.0" 16 | tox = "^4.11.1" 17 | ruff = "^0.7.4" 18 | 19 | [build-system] 20 | requires = ["poetry-core>=1.0.0,<2.0.0"] 21 | build-backend = "poetry.core.masonry.api" 22 | 23 | [tool.mypy] 24 | files = ["{{ package_name }}/**/*.py"] 25 | python_version = "3.10" 26 | disallow_untyped_defs = "True" 27 | disallow_any_unimported = "True" 28 | no_implicit_optional = "True" 29 | check_untyped_defs = "True" 30 | warn_return_any = "True" 31 | warn_unused_ignores = "True" 32 | show_error_codes = "True" 33 | ignore_missing_imports = "True" 34 | 35 | [tool.pytest.ini_options] 36 | testpaths = ["tests"] 37 | 38 | [tool.coverage.report] 39 | skip_empty = true 40 | 41 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/templates/{{ toolkit_name }}/tests/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/tests/test_{{ toolkit_name }}.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk.errors import ToolExecutionError 3 | 4 | from {{ package_name }}.tools.hello import say_hello 5 | 6 | 7 | def test_hello() -> None: 8 | assert say_hello("developer") == "Hello, developer!" 9 | 10 | 11 | def test_hello_raises_error() -> None: 12 | with pytest.raises(ToolExecutionError): 13 | say_hello(1) 14 | 15 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = true 3 | envlist = py310, py311, py312 4 | 5 | [gh-actions] 6 | python = 7 | 3.10: py310 8 | 3.11: py311 9 | 3.12: py312 10 | 11 | [testenv] 12 | passenv = PYTHON_VERSION 13 | allowlist_externals = poetry 14 | commands = 15 | poetry install -v --all-extras 16 | pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml 17 | 18 | -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/{{ package_name }}/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/templates/{{ toolkit_name }}/{{ package_name }}/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/{{ package_name }}/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/templates/{{ toolkit_name }}/{{ package_name }}/tools/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/templates/{{ toolkit_name }}/{{ package_name }}/tools/hello.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from arcade.sdk import tool 4 | 5 | 6 | @tool 7 | def say_hello(name: Annotated[str, "The name of the person to greet"]) -> str: 8 | """Say a greeting!""" 9 | 10 | return "Hello, " + name + "!" 11 | 12 | -------------------------------------------------------------------------------- /arcade/arcade/worker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/worker/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/worker/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/arcade/worker/core/__init__.py -------------------------------------------------------------------------------- /arcade/arcade/worker/fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | from .worker import FastAPIWorker 2 | 3 | __all__ = ["FastAPIWorker"] 4 | -------------------------------------------------------------------------------- /arcade/arcade/worker/fastapi/auth.py: -------------------------------------------------------------------------------- 1 | from fastapi import HTTPException 2 | from fastapi.security import HTTPAuthorizationCredentials 3 | 4 | from arcade.worker.core.auth import validate_engine_token 5 | 6 | 7 | # Dependency function to validate JWT 8 | async def validate_engine_request( 9 | worker_secret: str, 10 | credentials: HTTPAuthorizationCredentials, 11 | ) -> None: 12 | jwt: str = credentials.credentials 13 | validation_result = validate_engine_token(worker_secret, jwt) 14 | 15 | if not validation_result.valid: 16 | raise HTTPException( 17 | status_code=401, 18 | detail=f"Invalid token. Error: {validation_result.error}", 19 | headers={"WWW-Authenticate": "Bearer"}, 20 | ) 21 | -------------------------------------------------------------------------------- /arcade/arcade/worker/mcp/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MCP (Model Context Protocol) support for Arcade workers. 3 | """ 4 | 5 | from arcade.worker.mcp.stdio import StdioServer 6 | 7 | __all__ = ["StdioServer"] 8 | -------------------------------------------------------------------------------- /arcade/arcade/worker/utils.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import Any 3 | 4 | 5 | def is_async_callable(func: Any) -> bool: 6 | return asyncio.iscoroutinefunction(func) or ( 7 | callable(func) and asyncio.iscoroutinefunction(func.__call__) 8 | ) 9 | -------------------------------------------------------------------------------- /arcade/codecov.yaml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 70..100 3 | round: down 4 | precision: 1 5 | status: 6 | project: 7 | default: 8 | target: 90% 9 | threshold: 0.5% 10 | exclude: 11 | - arcade/cli/** 12 | -------------------------------------------------------------------------------- /arcade/poetry.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/poetry.toml -------------------------------------------------------------------------------- /arcade/run_cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from arcade.cli.main import cli 4 | 5 | if __name__ == "__main__": 6 | # Supports attaching debugger to cli. Run from ../.vscode/launch.json. 7 | if len(sys.argv) < 2: 8 | raise ValueError("At least one argument is required.") 9 | args = sys.argv[1:] 10 | cli(args) 11 | -------------------------------------------------------------------------------- /arcade/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/tests/__init__.py -------------------------------------------------------------------------------- /arcade/tests/cli/test_show.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | 3 | from arcade.cli.show import show_logic 4 | 5 | 6 | def test_show_logic_local_false(): 7 | with patch("arcade.cli.show.get_tools_from_engine") as mock_get_tools: 8 | mock_get_tools.return_value = [] 9 | show_logic( 10 | toolkit=None, 11 | tool=None, 12 | host="localhost", 13 | local=False, 14 | port=None, 15 | force_tls=False, 16 | force_no_tls=False, 17 | debug=False, 18 | ) 19 | 20 | # get_tools_from_engine should be called when local=False 21 | mock_get_tools.assert_called_once() 22 | 23 | 24 | def test_show_logic_local_true(): 25 | with patch("arcade.cli.show.create_cli_catalog") as mock_create_catalog: 26 | mock_create_catalog.return_value = [] 27 | 28 | show_logic( 29 | toolkit=None, 30 | tool=None, 31 | host="localhost", 32 | local=True, 33 | port=None, 34 | force_tls=False, 35 | force_no_tls=False, 36 | debug=False, 37 | ) 38 | 39 | # create_cli_catalog should be called when local=True 40 | mock_create_catalog.assert_called_once() 41 | -------------------------------------------------------------------------------- /arcade/tests/core/test_parse.py: -------------------------------------------------------------------------------- 1 | import ast 2 | 3 | import pytest 4 | 5 | from arcade.core.parse import get_tools_from_ast 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "source, expected_tools", 10 | [ 11 | pytest.param( 12 | """ 13 | @tool 14 | def my_function(): 15 | pass 16 | """, 17 | ["my_function"], 18 | id="function with tool decorator", 19 | ), 20 | pytest.param( 21 | """ 22 | import arcade.sdk as arc 23 | @arc.tool 24 | def another_function(): 25 | pass 26 | """, 27 | ["another_function"], 28 | id="function with arc.tool decorator", 29 | ), 30 | pytest.param( 31 | """ 32 | def no_decorator_function(): 33 | pass 34 | """, 35 | [], 36 | id="function without decorator", 37 | ), 38 | pytest.param( 39 | """ 40 | @other_decorator 41 | def different_function(): 42 | pass 43 | """, 44 | [], 45 | id="function with other decorator", 46 | ), 47 | ], 48 | ) 49 | def test_get_function_name_if_decorated(source, expected_tools): 50 | tree = ast.parse(source) 51 | tools = get_tools_from_ast(tree) 52 | assert tools == expected_tools 53 | -------------------------------------------------------------------------------- /arcade/tests/core/utils/test_casing.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from arcade.core.utils import pascal_to_snake_case, snake_to_pascal_case 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "input_str, expected", 8 | [ 9 | ("SnakeCase", "snake_case"), 10 | ("VeryLongSnake456", "very_long_snake456"), 11 | ], 12 | ) 13 | def test_pascal_to_snake_case(input_str: str, expected: str): 14 | assert pascal_to_snake_case(input_str) == expected 15 | 16 | 17 | @pytest.mark.parametrize( 18 | "input_str, expected", 19 | [ 20 | ("snake_case", "SnakeCase"), 21 | ("very_long_snake_456", "VeryLongSnake456"), 22 | ("camelCase", "Camelcase"), # camelCase isn't explicitly supported 23 | ], 24 | ) 25 | def test_snake_to_pascal_case(input_str: str, expected: str): 26 | assert snake_to_pascal_case(input_str) == expected 27 | -------------------------------------------------------------------------------- /arcade/tests/core/utils/test_is_strict_optional.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | import pytest 4 | 5 | from arcade.core.utils import is_strict_optional 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "type_input, expected", 10 | [ 11 | (Union[int, None], True), 12 | (Union[int, str, None], False), 13 | (Union[int, str], False), 14 | (Optional[int], True), 15 | (int | None, True), 16 | (None | int, True), 17 | (int | str, False), 18 | (int | str | None, False), 19 | (int, False), 20 | (str, False), 21 | (list, False), 22 | (dict, False), 23 | ], 24 | ) 25 | def test_is_optional_type(type_input, expected): 26 | assert is_strict_optional(type_input) == expected 27 | -------------------------------------------------------------------------------- /arcade/tests/core/utils/test_is_union.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | import pytest 4 | 5 | from arcade.core.utils import is_union 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "type_input, expected", 10 | [ 11 | (Union[int, str], True), 12 | (Optional[int], True), # Optional[int] is equivalent to Union[int, None] 13 | (int | str, True), 14 | (int | None, True), # int | None is equivalent to Optional[int] 15 | (int, False), 16 | (str, False), 17 | (list, False), 18 | (dict, False), 19 | ], 20 | ) 21 | def test_is_union(type_input, expected): 22 | assert is_union(type_input) == expected 23 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/env.secret.worker.toml: -------------------------------------------------------------------------------- 1 | 2 | [[worker]] 3 | [worker.config] 4 | id = "test" 5 | secret = "${env: TEST_WORKER_SECRET}" 6 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/full.worker.toml: -------------------------------------------------------------------------------- 1 | 2 | [[worker]] 3 | [worker.config] 4 | id = "test" 5 | enabled = true 6 | timeout = 10 7 | retries = 3 8 | secret = "test-secret" 9 | 10 | [worker.pypi_source] 11 | packages = ["arcade-x"] 12 | 13 | [worker.local_source] 14 | packages = ["./mock_toolkit"] 15 | 16 | [[worker.custom_source]] 17 | index = "pypi" 18 | index_url = "https://pypi.org/simple" 19 | trusted_host = "pypi.org" 20 | packages = ["arcade-ai>=1.0.0"] 21 | 22 | [[worker.custom_source]] 23 | index = "pypi2" 24 | index_url = "https://pypi2.org/simple" 25 | trusted_host = "pypi2.org" 26 | packages = ["arcade-slack"] 27 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/invalid.fields.worker.toml: -------------------------------------------------------------------------------- 1 | 2 | [[worker]] 3 | [worker.config] 4 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/invalid.localfile.worker.toml: -------------------------------------------------------------------------------- 1 | 2 | [[worker]] 3 | [worker.config] 4 | id = "test" 5 | enabled = true 6 | timeout = 10 7 | retries = 3 8 | secret = "test-secret" 9 | 10 | [worker.pypi_source] 11 | packages = ["arcade-ai"] 12 | 13 | [worker.local_source] 14 | packages = ["./missing_toolkit"] 15 | 16 | [[worker]] 17 | [worker.config] 18 | id = "test-2" 19 | enabled = true 20 | timeout = 10 21 | retries = 3 22 | secret = "test-secret" 23 | 24 | [worker.pypi_source] 25 | packages = ["arcade-ai"] 26 | 27 | [worker.local_source] 28 | packages = ["./invalid.localfile.worker.toml"] 29 | 30 | [[worker]] 31 | [worker.config] 32 | id = "test-3" 33 | enabled = true 34 | timeout = 10 35 | retries = 3 36 | secret = "test-secret" 37 | 38 | [worker.pypi_source] 39 | packages = ["arcade-ai"] 40 | 41 | [worker.local_source] 42 | packages = ["./invalid_toolkit"] 43 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/invalid.secret.worker.toml: -------------------------------------------------------------------------------- 1 | [[worker]] 2 | [worker.config] 3 | id = "test" 4 | enabled = true 5 | timeout = 10 6 | retries = 3 7 | secret = "dev" 8 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/invalid_toolkit/invalid_main.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/arcade/tests/deployment/test_files/invalid_toolkit/invalid_main.py -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/mock_toolkit/mock_main.py: -------------------------------------------------------------------------------- 1 | print("Hello, world!") 2 | -------------------------------------------------------------------------------- /arcade/tests/deployment/test_files/mock_toolkit/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mock_toolkit" 3 | version = "0.1.0" 4 | description = "Mock toolkit for Arcade" 5 | authors = ["Arcade "] 6 | -------------------------------------------------------------------------------- /arcade/tests/mcp/test_stdio.py: -------------------------------------------------------------------------------- 1 | import io 2 | import queue 3 | 4 | from arcade.worker.mcp.stdio import stdio_reader, stdio_writer 5 | 6 | 7 | def test_stdio_reader_puts_lines_and_none(): 8 | q: queue.Queue[str | None] = queue.Queue() 9 | test_input = io.StringIO("line1\nline2\n") 10 | 11 | stdio_reader(test_input, q) 12 | 13 | # We should get the two lines followed by None sentinel 14 | assert q.get_nowait() == "line1\n" 15 | assert q.get_nowait() == "line2\n" 16 | assert q.get_nowait() is None 17 | 18 | 19 | def test_stdio_writer_reads_until_none(): 20 | q: queue.Queue[str | None] = queue.Queue() 21 | output_stream = io.StringIO() 22 | 23 | # preload queue with two messages and sentinel 24 | q.put("msg1") 25 | q.put("msg2\n") 26 | q.put(None) 27 | 28 | stdio_writer(output_stream, q) 29 | 30 | # Ensure writer appended newlines when missing 31 | output_stream.seek(0) 32 | assert output_stream.read() == "msg1\nmsg2\n" 33 | -------------------------------------------------------------------------------- /arcade/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = true 3 | envlist = py310, py311, py312 4 | 5 | [gh-actions] 6 | python = 7 | 3.10: py310 8 | 3.11: py311 9 | 3.12: py312 10 | 11 | [testenv] 12 | passenv = PYTHON_VERSION 13 | allowlist_externals = poetry 14 | commands = 15 | poetry install -v --all-extras 16 | pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml 17 | # mypy 18 | -------------------------------------------------------------------------------- /contrib/crewai/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /contrib/crewai/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 6 |

7 |
8 |

CrewAI Integration

9 | 10 | License 11 | 12 | 13 | Downloads 14 | 15 | 16 |
17 | 18 |

19 | Docs • 20 | Toolkits • 21 | Python Client • 22 | JavaScript Client 23 |

24 | 25 | ## Overview 26 | 27 | `crewai-arcade` allows you to use Arcade tools in your CrewAI applications. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | pip install crewai-arcade 33 | ``` 34 | 35 | ## Usage 36 | 37 | See the [examples](https://github.com/ArcadeAI/arcade-ai/tree/main/examples/crewai) for usage examples 38 | -------------------------------------------------------------------------------- /contrib/crewai/crewai_arcade/__init__.py: -------------------------------------------------------------------------------- 1 | from .manager import ArcadeToolManager 2 | 3 | __all__ = ["ArcadeToolManager"] 4 | -------------------------------------------------------------------------------- /contrib/crewai/crewai_arcade/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/contrib/crewai/crewai_arcade/py.typed -------------------------------------------------------------------------------- /contrib/crewai/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "crewai-arcade" 3 | version = "0.1.1" 4 | description = "An integration package connecting Arcade and CrewAI" 5 | authors = ["Arcade "] 6 | readme = "README.md" 7 | repository = "https://github.com/arcadeai/arcade-ai/tree/main/contrib/crewai" 8 | license = "MIT" 9 | 10 | [tool.poetry.dependencies] 11 | python = ">=3.10,<3.13" 12 | crewai = ">=0.1.0,<1.0.0" 13 | pydantic = "^2.0.0" 14 | arcadepy = "^1.0.0" 15 | 16 | [tool.poetry.group.dev.dependencies] 17 | pytest = "^8.1.2" 18 | pytest-cov = "^4.0.0" 19 | mypy = "^1.5.1" 20 | pre-commit = "^3.4.0" 21 | tox = "^4.11.1" 22 | 23 | 24 | [tool.mypy] 25 | files = ["crewai_arcade"] 26 | python_version = "3.10" 27 | disallow_untyped_defs = "True" 28 | disallow_any_unimported = "True" 29 | no_implicit_optional = "True" 30 | check_untyped_defs = "True" 31 | warn_return_any = "True" 32 | warn_unused_ignores = "True" 33 | show_error_codes = "True" 34 | ignore_missing_imports = "True" 35 | 36 | [tool.pytest.ini_options] 37 | testpaths = ["tests"] 38 | 39 | 40 | [tool.coverage.run] 41 | branch = true 42 | source = ["crewai_arcade"] 43 | 44 | [tool.coverage.report] 45 | skip_empty = true 46 | -------------------------------------------------------------------------------- /contrib/crewai/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/contrib/crewai/tests/__init__.py -------------------------------------------------------------------------------- /contrib/langchain/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /contrib/langchain/langchain_arcade/__init__.py: -------------------------------------------------------------------------------- 1 | from .manager import ArcadeToolManager, AsyncToolManager, ToolManager 2 | 3 | __all__ = [ 4 | "ToolManager", 5 | "AsyncToolManager", 6 | "ArcadeToolManager", # Deprecated 7 | ] 8 | -------------------------------------------------------------------------------- /contrib/langchain/langchain_arcade/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/contrib/langchain/langchain_arcade/py.typed -------------------------------------------------------------------------------- /contrib/langchain/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "langchain-arcade" 3 | version = "1.3.1" 4 | description = "An integration package connecting Arcade and Langchain/LangGraph" 5 | authors = ["Arcade "] 6 | readme = "README.md" 7 | repository = "https://github.com/arcadeai/arcade-ai/tree/main/contrib/langchain" 8 | license = "MIT" 9 | 10 | [tool.poetry.dependencies] 11 | python = ">=3.10,<4" 12 | arcadepy = "1.3.*" 13 | langchain-core = ">=0.3.49,<0.4" 14 | 15 | 16 | [tool.poetry.group.dev.dependencies] 17 | pytest = "^8.1.2" 18 | pytest-cov = "^4.0.0" 19 | mypy = "^1.5.1" 20 | pre-commit = "^3.4.0" 21 | tox = "^4.11.1" 22 | pytest-asyncio = "^0.23.7" 23 | langgraph = ">=0.3.23,<0.4" 24 | 25 | 26 | [tool.mypy] 27 | files = ["langchain_arcade"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | 42 | [tool.coverage.run] 43 | branch = true 44 | source = ["langchain_arcade"] 45 | 46 | [tool.coverage.report] 47 | skip_empty = true 48 | -------------------------------------------------------------------------------- /contrib/langchain/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | from arcadepy import Arcade 5 | 6 | 7 | @pytest.fixture(scope="session") 8 | def arcade_base_url(): 9 | """ 10 | Retrieve the ARCADE_BASE_URL from the environment, falling back to a default 11 | if not found. 12 | """ 13 | return os.getenv("ARCADE_BASE_URL", "http://localhost:9099") 14 | 15 | 16 | @pytest.fixture(scope="session") 17 | def arcade_api_key(): 18 | """ 19 | Retrieve the ARCADE_API_KEY from the environment, falling back to a default 20 | if not found. 21 | """ 22 | return os.getenv("ARCADE_API_KEY", "test_api_key") 23 | 24 | 25 | @pytest.fixture(scope="session") 26 | def arcade_client(arcade_base_url, arcade_api_key): 27 | """ 28 | Creates a single Arcade client instance for use in all tests. 29 | Any method calls on this client can be patched/mocked within the tests. 30 | """ 31 | client = Arcade(api_key=arcade_api_key, base_url=arcade_base_url) 32 | yield client 33 | # Teardown logic would go here if necessary 34 | -------------------------------------------------------------------------------- /contrib/langchain/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = true 3 | envlist = py310, py311, py312 4 | 5 | [gh-actions] 6 | python = 7 | 3.10: py310 8 | 3.11: py311 9 | 3.12: py312 10 | 11 | [testenv] 12 | passenv = PYTHON_VERSION 13 | allowlist_externals = poetry 14 | commands = 15 | poetry install -v --all-extras 16 | pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml 17 | -------------------------------------------------------------------------------- /cspell.config.yaml: -------------------------------------------------------------------------------- 1 | version: "0.2" 2 | ignorePaths: 3 | - pyproject.toml 4 | dictionaryDefinitions: [] 5 | dictionaries: [] 6 | words: 7 | - arcadepy 8 | - conlist 9 | - fastapi 10 | - httpx 11 | - openai 12 | - pydantic 13 | - pyproject 14 | - toolpack 15 | ignoreWords: [] 16 | import: [] 17 | -------------------------------------------------------------------------------- /docker/env.example: -------------------------------------------------------------------------------- 1 | 2 | ### LLM ### 3 | 4 | OPENAI_API_KEY= 5 | ANTHROPIC_API_KEY= 6 | 7 | ### Auth providers ### 8 | 9 | GITHUB_CLIENT_ID= 10 | GITHUB_CLIENT_SECRET= 11 | 12 | GOOGLE_CLIENT_ID="" 13 | GOOGLE_CLIENT_SECRET= 14 | 15 | LINKEDIN_CLIENT_ID= 16 | LINKEDIN_CLIENT_SECRET="" 17 | 18 | MICROSOFT_CLIENT_ID= 19 | MICROSOFT_CLIENT_SECRET="" 20 | 21 | SLACK_CLIENT_ID="" 22 | SLACK_CLIENT_SECRET= 23 | 24 | SPOTIFY_CLIENT_ID= 25 | SPOTIFY_CLIENT_SECRET= 26 | 27 | X_CLIENT_ID= 28 | X_CLIENT_SECRET= 29 | 30 | ZOOM_CLIENT_ID= 31 | ZOOM_CLIENT_SECRET= 32 | 33 | ### WORKER ### 34 | 35 | WORKER_URL="http://arcade-worker:8002" 36 | 37 | ### Redis ### 38 | 39 | REDIS_HOST=arcade-redis 40 | REDIS_PORT=6379 41 | REDIS_PASSWORD="" 42 | RATE_LIMIT_REQS_PER_MIN=100 43 | 44 | ### ARCADE ### 45 | 46 | ARCADE_HOME=/app 47 | ARCADE_WORKER_SECRET=dev 48 | ROOT_KEY_1=supersecret 49 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting arcade..." 4 | arcade serve --host $HOST --port $PORT $([ "$OTEL_ENABLE" = "true" ] && echo "--otel-enable") 5 | -------------------------------------------------------------------------------- /docker/toolkits.txt: -------------------------------------------------------------------------------- 1 | arcade-asana 2 | arcade-code-sandbox 3 | arcade-confluence 4 | arcade-dropbox 5 | arcade-github 6 | arcade-google 7 | arcade-hubspot 8 | arcade-linkedin 9 | arcade-math 10 | arcade-microsoft 11 | arcade-notion-toolkit 12 | arcade-reddit 13 | arcade-salesforce 14 | arcade-search 15 | arcade-slack 16 | arcade-spotify 17 | arcade-stripe 18 | arcade-web 19 | arcade-x 20 | arcade-zoom 21 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Un-ignore poetry.lock because the projects in this directory represent runnable sample apps 2 | !poetry.lock 3 | -------------------------------------------------------------------------------- /examples/ai-sdk/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=sk-proj-1234567890 2 | ARCADE_API_KEY=arc_1234567890 3 | -------------------------------------------------------------------------------- /examples/ai-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Environment variables 8 | .env 9 | .env.local 10 | .env.*.local 11 | 12 | # Build output 13 | dist/ 14 | build/ 15 | out/ 16 | 17 | # IDE and editor files 18 | .idea/ 19 | .vscode/ 20 | *.swp 21 | *.swo 22 | .DS_Store 23 | 24 | # Logs 25 | logs/ 26 | *.log 27 | 28 | # Testing 29 | coverage/ 30 | 31 | # Temporary files 32 | tmp/ 33 | temp/ 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | -------------------------------------------------------------------------------- /examples/ai-sdk/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /examples/ai-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arcade-ai-sdk", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "node --env-file=.env index.js", 9 | "generateText": "node --env-file=.env generateText.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "packageManager": "pnpm@10.6.5", 15 | "dependencies": { 16 | "@ai-sdk/openai": "^1.3.22", 17 | "@arcadeai/arcadejs": "latest", 18 | "ai": "^4.3.15" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/crewai/requirements.txt: -------------------------------------------------------------------------------- 1 | crewai-arcade>=0.1.0 2 | -------------------------------------------------------------------------------- /examples/langchain-ts/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=sk-proj-1234567890 2 | ARCADE_API_KEY=arc_1234567890 3 | -------------------------------------------------------------------------------- /examples/langchain-ts/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Environment variables 8 | .env 9 | .env.local 10 | .env.*.local 11 | 12 | # Build output 13 | dist/ 14 | build/ 15 | out/ 16 | 17 | # IDE and editor files 18 | .idea/ 19 | .vscode/ 20 | *.swp 21 | *.swo 22 | .DS_Store 23 | 24 | # Logs 25 | logs/ 26 | *.log 27 | 28 | # Testing 29 | coverage/ 30 | 31 | # Temporary files 32 | tmp/ 33 | temp/ 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # LangGraph API 45 | .langgraph_api 46 | -------------------------------------------------------------------------------- /examples/langchain-ts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /examples/langchain-ts/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "agent": "./langgraph-with-user-auth.ts:graph" 7 | }, 8 | "env": ".env" 9 | } 10 | -------------------------------------------------------------------------------- /examples/langchain-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "langchain-js", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "npx tsx --env-file=.env langgraph-arcade-minimal.ts", 9 | "dev-auth": "npx tsx --env-file=.env langgraph-with-user-auth.ts", 10 | "dev-auth-langchain": "npx tsx --env-file=.env langchain-tool-arcade-auth.ts", 11 | "studio": "npx @langchain/langgraph-cli@latest dev" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "packageManager": "pnpm@10.6.5", 17 | "dependencies": { 18 | "@arcadeai/arcadejs": "latest", 19 | "@langchain/community": "^0.3.42", 20 | "@langchain/core": "^0.3.55", 21 | "@langchain/langgraph": "^0.2.72", 22 | "@langchain/openai": "^0.5.10" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^22.15.17" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/langchain-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "outDir": "dist", 11 | "rootDir": ".", 12 | "types": ["node"], 13 | "lib": ["ES2020"], 14 | "declaration": true, 15 | "sourceMap": true, 16 | "resolveJsonModule": true 17 | }, 18 | "include": ["./**/*.ts"], 19 | "exclude": ["node_modules", "dist"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/langchain/requirements.txt: -------------------------------------------------------------------------------- 1 | langchain-google-community[gmail]>=0.1.1 2 | langchain-openai>=0.1.1 3 | langchain-arcade>=1.2.0 4 | -------------------------------------------------------------------------------- /examples/langchain/studio/README.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 3 | ### Environment 4 | 5 | Copy the `env.example` file to `.env` and supply your API keys for **at least** `OPENAI_API_KEY` and `ARCADE_API_KEY`. 6 | 7 | - Arcade API key: `ARCADE_API_KEY` (instructions [here](https://docs.arcade.dev/home/api-keys)) 8 | - OpenAI API key: `OPENAI_API_KEY` (instructions [here](https://platform.openai.com/docs/quickstart)) 9 | 10 | ## Usage with LangGraph API 11 | 12 | ### Local testing with LangGraph Studio 13 | 14 | [Download LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download) and open this directory in the Studio application. 15 | 16 | The `langgraph.json` file in this directory specifies the graph that will be loaded in Studio. 17 | 18 | ### Deploying to LangGraph Cloud 19 | 20 | Follow [these instructions](https://langchain-ai.github.io/langgraph/cloud/quick_start/#deploy-to-cloud) to deploy your graph to LangGraph Cloud. 21 | -------------------------------------------------------------------------------- /examples/langchain/studio/configuration.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | 4 | @dataclass(kw_only=True) 5 | class AgentConfigurable: 6 | """The configurable fields for the chatbot.""" 7 | 8 | user_id: str = "default-user" 9 | -------------------------------------------------------------------------------- /examples/langchain/studio/env.example: -------------------------------------------------------------------------------- 1 | # To separate your traces from other application 2 | LANGSMITH_PROJECT=arcade-graph 3 | # LANGCHAIN_API_KEY=... 4 | 5 | # Arcade API key 6 | # ARCADE_API_KEY=... 7 | 8 | # LLM choice: 9 | # OPENAI_API_KEY=... 10 | -------------------------------------------------------------------------------- /examples/langchain/studio/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerfile_lines": [], 3 | "graphs": { 4 | "graph": "./graph.py:graph" 5 | }, 6 | "env": ".env", 7 | "python_version": "3.11", 8 | "dependencies": [ 9 | "." 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /examples/langchain/studio/requirements.txt: -------------------------------------------------------------------------------- 1 | langchain>=0.3.0 2 | langchain-openai>=0.1.1 3 | langchain_arcade>=1.0.0 4 | langgraph>=0.2.67 5 | -------------------------------------------------------------------------------- /examples/langgraph-ts/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | .pnpm-store/ 4 | 5 | # Environment variables 6 | .env 7 | .env.local 8 | .env.development.local 9 | .env.test.local 10 | .env.production.local 11 | .env.example 12 | 13 | # Build outputs 14 | dist/ 15 | build/ 16 | out/ 17 | 18 | # Logs 19 | logs 20 | *.log 21 | npm-debug.log* 22 | pnpm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | lerna-debug.log* 26 | 27 | # Editor directories and files 28 | .idea/ 29 | .vscode/ 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | *.sublime-workspace 36 | *.sublime-project 37 | .project 38 | .classpath 39 | .settings/ 40 | 41 | # OS specific 42 | .DS_Store 43 | Thumbs.db 44 | ehthumbs.db 45 | Desktop.ini 46 | 47 | # pnpm specific 48 | .pnpm-debug.log 49 | pnpm-lock.yaml 50 | 51 | # Testing 52 | coverage/ 53 | 54 | # Cache 55 | .cache/ 56 | .turbo/ 57 | 58 | # LangGraph API 59 | .langgraph_api/ 60 | 61 | # Temporary files 62 | *.tmp 63 | *.temp 64 | *.bak 65 | *.swp 66 | *~ 67 | 68 | # Local development 69 | .local/ 70 | -------------------------------------------------------------------------------- /examples/langgraph-ts/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "agent": "./src/graph.ts:graph" 7 | }, 8 | "env": ".env" 9 | } 10 | -------------------------------------------------------------------------------- /examples/langgraph-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arcade-langgraph", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "npx @langchain/langgraph-cli dev" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "packageManager": "pnpm@10.10.0", 14 | "dependencies": { 15 | "@arcadeai/arcadejs": "latest", 16 | "@langchain/community": "^0.3.42", 17 | "@langchain/core": "^0.3.55", 18 | "@langchain/langgraph": "^0.2.71", 19 | "langchain": "^0.3.24", 20 | "@langchain/openai": "^0.5.10", 21 | "dotenv": "^16.5.0", 22 | "zod": "^3.24.4" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^22.15.17" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/langgraph-ts/src/configuration.ts: -------------------------------------------------------------------------------- 1 | import type { RunnableConfig } from "@langchain/core/runnables"; 2 | /** 3 | * Define the configurable parameters for the agent. 4 | */ 5 | import { Annotation } from "@langchain/langgraph"; 6 | import { SYSTEM_PROMPT_TEMPLATE } from "./prompts.js"; 7 | 8 | export const ConfigurationSchema = Annotation.Root({ 9 | /** 10 | * The system prompt to be used by the agent. 11 | */ 12 | systemPromptTemplate: Annotation, 13 | 14 | /** 15 | * The name of the language model to be used by the agent. 16 | */ 17 | model: Annotation, 18 | }); 19 | 20 | export function ensureConfiguration( 21 | config: RunnableConfig, 22 | ): typeof ConfigurationSchema.State { 23 | /** 24 | * Ensure the defaults are populated. 25 | */ 26 | const configurable = config.configurable ?? {}; 27 | return { 28 | systemPromptTemplate: 29 | configurable.systemPromptTemplate ?? SYSTEM_PROMPT_TEMPLATE, 30 | model: configurable.model ?? "gpt-4o-mini", 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /examples/langgraph-ts/src/prompts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default prompts used by the agent. 3 | */ 4 | 5 | export const SYSTEM_PROMPT_TEMPLATE = `You are a helpful AI assistant. 6 | 7 | System time: {system_time}`; 8 | -------------------------------------------------------------------------------- /examples/langgraph-ts/studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/examples/langgraph-ts/studio.png -------------------------------------------------------------------------------- /examples/mastra/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=sk-proj-1234567890 2 | ARCADE_API_KEY=arc_1234567890 3 | -------------------------------------------------------------------------------- /examples/mastra/.gitignore: -------------------------------------------------------------------------------- 1 | output.txt 2 | node_modules 3 | dist 4 | .mastra 5 | .env.development 6 | .env 7 | *.db 8 | *.db-* 9 | -------------------------------------------------------------------------------- /examples/mastra/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /examples/mastra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mastra", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "mastra dev", 9 | "build": "mastra build" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "type": "module", 15 | "dependencies": { 16 | "@ai-sdk/openai": "^1.3.22", 17 | "@arcadeai/arcadejs": "^1.5.0", 18 | "@mastra/core": "0.9.4-alpha.1", 19 | "@mastra/libsql": "0.0.4-alpha.1", 20 | "@mastra/memory": "0.3.4-alpha.1", 21 | "zod": "^3.24.4" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^22.15.17", 25 | "mastra": "0.6.3-alpha.1", 26 | "typescript": "^5.8.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/mastra/src/mastra/index.ts: -------------------------------------------------------------------------------- 1 | import { Mastra } from "@mastra/core"; 2 | import { LibSQLStore } from "@mastra/libsql"; 3 | import { googleAgent } from "./agents/google"; 4 | 5 | export const mastra = new Mastra({ 6 | agents: { googleAgent }, 7 | storage: new LibSQLStore({ 8 | url: "file:../mastra.db", 9 | }), 10 | }); 11 | -------------------------------------------------------------------------------- /examples/mastra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ES2022", 5 | "moduleResolution": "bundler", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "noEmit": true, 11 | "outDir": "dist" 12 | }, 13 | "include": [ 14 | "src/**/*" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/mcp/claude.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "arcade": { 4 | "command": "bash", 5 | "args": ["-c", "export ARCADE_API_KEY=arc_xxxx && /path/to/python /path/to/arcade mcp"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/mcp/run_stdio.py: -------------------------------------------------------------------------------- 1 | import arcade_google # pip install arcade_google 2 | import arcade_search # pip install arcade_search 3 | 4 | from arcade.core.catalog import ToolCatalog 5 | from arcade.worker.mcp.stdio import StdioServer 6 | 7 | # 2. Create and populate the tool catalog 8 | catalog = ToolCatalog() 9 | catalog.add_module(arcade_google) # Registers all tools in the package 10 | catalog.add_module(arcade_search) 11 | 12 | 13 | # 3. Main entrypoint 14 | async def main(): 15 | # Create the worker with the tool catalog 16 | worker = StdioServer(catalog) 17 | 18 | # Run the worker 19 | await worker.run() 20 | 21 | 22 | if __name__ == "__main__": 23 | import asyncio 24 | 25 | asyncio.run(main()) 26 | -------------------------------------------------------------------------------- /examples/serving-tools/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION=latest 2 | 3 | # Base worker image 4 | FROM ghcr.io/arcadeai/worker-base:${VERSION} 5 | 6 | # Copy requirements and constraints 7 | COPY toolkits.txt ./ 8 | 9 | # Install toolkits from file 10 | RUN pip install -r toolkits.txt 11 | -------------------------------------------------------------------------------- /examples/serving-tools/docker/README.md: -------------------------------------------------------------------------------- 1 | ## Custom Worker Image 2 | 3 | This example shows how to build a custom worker image with toolkits. 4 | 5 | ### Requirements 6 | 7 | - Docker 8 | 9 | ### Build 10 | 11 | ``` 12 | docker build -t custom-worker:0.1.0 . 13 | ``` 14 | 15 | ### Run 16 | 17 | ``` 18 | docker run -p 8002:8002 custom-worker:0.1.0 19 | ``` 20 | 21 | ### Change the Toolkits 22 | 23 | To change the toolkits, edit the `toolkits.txt` file. 24 | 25 | ``` 26 | arcade-google==0.1.0 27 | arcade-web==0.1.0 28 | arcade-zoom==0.1.2 29 | ... 30 | ``` 31 | -------------------------------------------------------------------------------- /examples/serving-tools/docker/toolkits.txt: -------------------------------------------------------------------------------- 1 | arcade-google 2 | -------------------------------------------------------------------------------- /examples/serving-tools/modal/README.md: -------------------------------------------------------------------------------- 1 | ## Deploy a Custom Arcade Worker on Modal 2 | 3 | ### Requirements 4 | 5 | - Python 3.10+ 6 | - Modal CLI 7 | 8 | ### Deploy 9 | 10 | ```bash 11 | cd examples/serving-tools 12 | modal deploy run-arcade-worker.py 13 | ``` 14 | 15 | ### Changing the Toolkits 16 | 17 | To change the toolkits, edit the `toolkits` list in the `run-arcade-worker.py` file. 18 | -------------------------------------------------------------------------------- /examples/serving-tools/modal/run-arcade-worker.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from modal import App, Image, asgi_app 4 | 5 | # Define the FastAPI app 6 | app = App("arcade-worker") 7 | 8 | toolkits = ["arcade_google", "arcade_slack"] 9 | 10 | image = Image.debian_slim().pip_install("arcade-ai").pip_install(toolkits) 11 | 12 | 13 | @app.function(image=image) 14 | @asgi_app() 15 | def fastapi_app(): 16 | from fastapi import FastAPI 17 | 18 | from arcade.sdk import Toolkit 19 | from arcade.worker.fastapi.worker import FastAPIWorker 20 | 21 | web_app = FastAPI() 22 | 23 | # Initialize app and Arcade FastAPIWorker 24 | worker_secret = os.environ.get("ARCADE_WORKER_SECRET", "dev") 25 | worker = FastAPIWorker(web_app, secret=worker_secret) 26 | 27 | # Register toolkits we've installed 28 | installed_toolkits = Toolkit.find_all_arcade_toolkits() 29 | for toolkit in installed_toolkits: 30 | if toolkit.package_name in toolkits: 31 | worker.register_toolkit(toolkit) 32 | 33 | return web_app 34 | -------------------------------------------------------------------------------- /toolkits/TOOLKIT_RELEASE_MANAGERS.txt: -------------------------------------------------------------------------------- 1 | Spartee 2 | nbarbettini 3 | EricGustin 4 | sdreyer 5 | wdawson 6 | byrro 7 | torresmateo 8 | -------------------------------------------------------------------------------- /toolkits/asana/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/asana/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py39" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/asana/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Arcade 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 | -------------------------------------------------------------------------------- /toolkits/asana/arcade_asana/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/asana/arcade_asana/__init__.py -------------------------------------------------------------------------------- /toolkits/asana/arcade_asana/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from typing import Any, Callable 3 | 4 | 5 | def clean_asana_response(func: Callable[..., Any]) -> Callable[..., Any]: 6 | def response_cleaner(data: dict[str, Any]) -> dict[str, Any]: 7 | if "gid" in data: 8 | data["id"] = data["gid"] 9 | del data["gid"] 10 | 11 | for k, v in data.items(): 12 | if isinstance(v, dict): 13 | data[k] = response_cleaner(v) 14 | elif isinstance(v, list): 15 | data[k] = [ 16 | item if not isinstance(item, dict) else response_cleaner(item) for item in v 17 | ] 18 | 19 | return data 20 | 21 | @wraps(func) 22 | async def wrapper(*args: Any, **kwargs: Any) -> Any: 23 | response = await func(*args, **kwargs) 24 | return response_cleaner(response) 25 | 26 | return wrapper 27 | -------------------------------------------------------------------------------- /toolkits/asana/arcade_asana/exceptions.py: -------------------------------------------------------------------------------- 1 | from arcade.sdk.errors import ToolExecutionError 2 | 3 | 4 | class AsanaToolExecutionError(ToolExecutionError): 5 | pass 6 | 7 | 8 | class PaginationTimeoutError(AsanaToolExecutionError): 9 | def __init__(self, timeout_seconds: int, tool_name: str): 10 | message = f"Pagination timed out after {timeout_seconds} seconds" 11 | super().__init__( 12 | message=message, 13 | developer_message=f"{message} while calling the tool {tool_name}", 14 | ) 15 | -------------------------------------------------------------------------------- /toolkits/asana/arcade_asana/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_asana.tools.projects import get_project_by_id, list_projects 2 | from arcade_asana.tools.tags import create_tag, list_tags 3 | from arcade_asana.tools.tasks import ( 4 | attach_file_to_task, 5 | create_task, 6 | get_subtasks_from_a_task, 7 | get_task_by_id, 8 | get_tasks_without_id, 9 | update_task, 10 | ) 11 | from arcade_asana.tools.teams import get_team_by_id, list_teams_the_current_user_is_a_member_of 12 | from arcade_asana.tools.users import get_user_by_id, list_users 13 | from arcade_asana.tools.workspaces import list_workspaces 14 | 15 | __all__ = [ 16 | "attach_file_to_task", 17 | "create_tag", 18 | "create_task", 19 | "get_project_by_id", 20 | "get_subtasks_from_a_task", 21 | "get_task_by_id", 22 | "get_team_by_id", 23 | "get_user_by_id", 24 | "list_projects", 25 | "list_tags", 26 | "list_teams_the_current_user_is_a_member_of", 27 | "list_users", 28 | "list_workspaces", 29 | "get_tasks_without_id", 30 | "update_task", 31 | ] 32 | -------------------------------------------------------------------------------- /toolkits/asana/conftest.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | 3 | import pytest 4 | from arcade.sdk import ToolAuthorizationContext, ToolContext 5 | 6 | 7 | @pytest.fixture 8 | def mock_context(): 9 | mock_auth = ToolAuthorizationContext(token="fake-token") # noqa: S106 10 | return ToolContext(authorization=mock_auth) 11 | 12 | 13 | @pytest.fixture 14 | def mock_httpx_client(): 15 | with patch("arcade_asana.models.httpx") as mock_httpx: 16 | yield mock_httpx.AsyncClient().__aenter__.return_value 17 | -------------------------------------------------------------------------------- /toolkits/asana/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_asana" 3 | version = "0.1.0" 4 | description = "Arcade tools designed for LLMs to interact with Asana" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.4.0,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_asana/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/arcade_code_sandbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/code_sandbox/arcade_code_sandbox/__init__.py -------------------------------------------------------------------------------- /toolkits/code_sandbox/arcade_code_sandbox/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/code_sandbox/arcade_code_sandbox/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/code_sandbox/arcade_code_sandbox/tools/models.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | # Models and enums for the e2b code interpreter 5 | class E2BSupportedLanguage(str, Enum): 6 | PYTHON = "python" 7 | JAVASCRIPT = "js" 8 | R = "r" 9 | JAVA = "java" 10 | BASH = "bash" 11 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_code_sandbox" 3 | version = "1.0.0" 4 | description = "Arcade.dev LLM tools for running code in a sandbox" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | e2b-code-interpreter = "^1.0.1" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_code_sandbox/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/code_sandbox/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/code_sandbox/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/confluence/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/confluence/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | -------------------------------------------------------------------------------- /toolkits/confluence/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade 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 | -------------------------------------------------------------------------------- /toolkits/confluence/arcade_confluence/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/confluence/arcade_confluence/__init__.py -------------------------------------------------------------------------------- /toolkits/confluence/arcade_confluence/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_confluence.tools.attachment import get_attachments_for_page, list_attachments 2 | from arcade_confluence.tools.page import ( 3 | create_page, 4 | get_page, 5 | get_pages_by_id, 6 | list_pages, 7 | rename_page, 8 | update_page_content, 9 | ) 10 | from arcade_confluence.tools.search import search_content 11 | from arcade_confluence.tools.space import get_space, get_space_hierarchy, list_spaces 12 | 13 | __all__ = [ 14 | # Attachment 15 | "get_attachments_for_page", 16 | "list_attachments", 17 | # Page 18 | "create_page", 19 | "get_pages_by_id", 20 | "get_page", 21 | "list_pages", 22 | "rename_page", 23 | "update_page_content", 24 | # Search 25 | "search_content", 26 | # Space 27 | "get_space", 28 | "get_space_hierarchy", 29 | "list_spaces", 30 | ] 31 | -------------------------------------------------------------------------------- /toolkits/confluence/evals/critics.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Any 3 | 4 | from arcade.sdk.eval.critic import Critic 5 | 6 | 7 | @dataclass 8 | class ListCritic(Critic): 9 | """ 10 | A critic for comparing two lists. 11 | """ 12 | 13 | def __init__( 14 | self, 15 | critic_field: str, 16 | weight: float = 1.0, 17 | order_matters: bool = True, 18 | duplicates_matter: bool = True, 19 | case_sensitive: bool = False, 20 | ): 21 | self.critic_field = critic_field 22 | self.weight = weight 23 | self.order_matters = order_matters 24 | self.duplicates_matter = duplicates_matter 25 | self.case_sensitive = case_sensitive 26 | 27 | def evaluate(self, expected: list[Any], actual: list[Any]) -> dict[str, float | bool]: 28 | if not self.case_sensitive: 29 | actual = [item.lower() for item in actual] 30 | expected = [item.lower() for item in expected] 31 | 32 | match = actual == expected if self.order_matters else set(actual) == set(expected) 33 | if self.duplicates_matter: 34 | match = match and len(actual) == len(expected) 35 | 36 | return {"match": match, "score": self.weight if match else 0.0} 37 | -------------------------------------------------------------------------------- /toolkits/confluence/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_confluence" 3 | version = "0.0.1" 4 | description = "Arcade.dev LLM tools for Confluence" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = "^1.0.5" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | mypy = "^1.5.1" 16 | pre-commit = "^3.4.0" 17 | tox = "^4.11.1" 18 | ruff = "^0.7.4" 19 | pytest-asyncio = "^0.24.0" 20 | 21 | [build-system] 22 | requires = ["poetry-core>=1.0.0,<2.0.0"] 23 | build-backend = "poetry.core.masonry.api" 24 | 25 | [tool.mypy] 26 | files = ["arcade_confluence/**/*.py"] 27 | python_version = "3.10" 28 | disallow_untyped_defs = "True" 29 | disallow_any_unimported = "True" 30 | no_implicit_optional = "True" 31 | check_untyped_defs = "True" 32 | warn_return_any = "True" 33 | warn_unused_ignores = "True" 34 | show_error_codes = "True" 35 | ignore_missing_imports = "True" 36 | 37 | [tool.pytest.ini_options] 38 | testpaths = ["tests"] 39 | 40 | [tool.coverage.report] 41 | skip_empty = true 42 | -------------------------------------------------------------------------------- /toolkits/confluence/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/confluence/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/dropbox/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/dropbox/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py39" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/dropbox/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Arcade 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 | -------------------------------------------------------------------------------- /toolkits/dropbox/arcade_dropbox/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/dropbox/arcade_dropbox/__init__.py -------------------------------------------------------------------------------- /toolkits/dropbox/arcade_dropbox/constants.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class EndpointType(Enum): 5 | API = "api" 6 | CONTENT = "content" 7 | 8 | 9 | class Endpoint(Enum): 10 | LIST_FOLDER = "/files/list_folder" 11 | SEARCH_FILES = "/files/search" 12 | DOWNLOAD_FILE = "/files/download" 13 | 14 | 15 | class ItemCategory(Enum): 16 | IMAGE = "image" 17 | DOCUMENT = "document" 18 | PDF = "pdf" 19 | SPREADSHEET = "spreadsheet" 20 | PRESENTATION = "presentation" 21 | AUDIO = "audio" 22 | VIDEO = "video" 23 | FOLDER = "folder" 24 | PAPER = "paper" 25 | 26 | 27 | API_BASE_URL = "https://{endpoint_type}.dropboxapi.com" 28 | API_VERSION = "2" 29 | ENDPOINT_URL_MAP = { 30 | Endpoint.LIST_FOLDER: (EndpointType.API, "files/list_folder"), 31 | Endpoint.SEARCH_FILES: (EndpointType.API, "files/search_v2"), 32 | Endpoint.DOWNLOAD_FILE: (EndpointType.CONTENT, "files/download"), 33 | } 34 | MAX_RESPONSE_BODY_SIZE = 10 * 1024 * 1024 # 10 MiB 35 | -------------------------------------------------------------------------------- /toolkits/dropbox/arcade_dropbox/exceptions.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class DropboxApiError(Exception): 5 | def __init__( 6 | self, 7 | status_code: int, 8 | error_summary: str, 9 | user_message: Optional[str], 10 | ): 11 | if "path/not_found" in error_summary: 12 | self.message = "The specified path was not found by Dropbox" 13 | elif "unsupported_file" in error_summary: 14 | self.message = "The specified file is not supported for the requested operation" 15 | else: 16 | self.message = user_message or error_summary 17 | 18 | self.status_code = status_code 19 | -------------------------------------------------------------------------------- /toolkits/dropbox/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_dropbox" 3 | version = "0.1.1" 4 | description = "Arcade tools designed for LLMs to interact with Dropbox" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_dropbox/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/github/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/github/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/github/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/github/arcade_github/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/github/arcade_github/__init__.py -------------------------------------------------------------------------------- /toolkits/github/arcade_github/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/github/arcade_github/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/github/arcade_github/tools/constants.py: -------------------------------------------------------------------------------- 1 | # Base URL for GitHub API 2 | GITHUB_API_BASE_URL = "https://api.github.com" 3 | 4 | # Endpoint patterns 5 | ENDPOINTS = { 6 | "repo": "/repos/{owner}/{repo}", 7 | "org_repos": "/orgs/{org}/repos", 8 | "repo_activity": "/repos/{owner}/{repo}/activity", 9 | "repo_pulls_comments": "/repos/{owner}/{repo}/pulls/comments", 10 | "repo_issues": "/repos/{owner}/{repo}/issues", 11 | "repo_issue_comments": "/repos/{owner}/{repo}/issues/{issue_number}/comments", 12 | "repo_pulls": "/repos/{owner}/{repo}/pulls", 13 | "repo_pull": "/repos/{owner}/{repo}/pulls/{pull_number}", 14 | "repo_pull_commits": "/repos/{owner}/{repo}/pulls/{pull_number}/commits", 15 | "repo_pull_comments": "/repos/{owner}/{repo}/pulls/{pull_number}/comments", 16 | "repo_pull_comment_replies": "/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies", # noqa: E501 17 | "user_starred": "/user/starred/{owner}/{repo}", 18 | "repo_stargazers": "/repos/{owner}/{repo}/stargazers", 19 | } 20 | -------------------------------------------------------------------------------- /toolkits/github/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_github" 3 | version = "0.1.10" 4 | description = "Arcade.dev LLM tools for Github" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=0.1,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_github/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/github/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/github/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/google/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/google/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/google/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Arcade 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 | -------------------------------------------------------------------------------- /toolkits/google/arcade_google/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/google/arcade_google/__init__.py -------------------------------------------------------------------------------- /toolkits/google/arcade_google/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from arcade_google.models import GmailReplyToWhom 4 | 5 | # The default reply in Gmail is to only the sender. Since Gmail also offers the possibility of 6 | # changing the default to 'reply to all', we support both options through an env variable. 7 | # https://support.google.com/mail/answer/6585?hl=en&sjid=15399867888091633568-SA#null 8 | try: 9 | GMAIL_DEFAULT_REPLY_TO = GmailReplyToWhom( 10 | # Values accepted are defined in the arcade_google.tools.models.GmailReplyToWhom Enum 11 | os.getenv("ARCADE_GMAIL_DEFAULT_REPLY_TO", GmailReplyToWhom.ONLY_THE_SENDER.value).lower() 12 | ) 13 | except ValueError as e: 14 | raise ValueError( 15 | "Invalid value for ARCADE_GMAIL_DEFAULT_REPLY_TO: " 16 | f"'{os.getenv('ARCADE_GMAIL_DEFAULT_REPLY_TO')}'. Expected one of " 17 | f"{list(GmailReplyToWhom.__members__.keys())}" 18 | ) from e 19 | 20 | 21 | DEFAULT_SEARCH_CONTACTS_LIMIT = 30 22 | 23 | DEFAULT_SHEET_ROW_COUNT = 1000 24 | DEFAULT_SHEET_COLUMN_COUNT = 26 25 | -------------------------------------------------------------------------------- /toolkits/google/arcade_google/enums.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/google/arcade_google/enums.py -------------------------------------------------------------------------------- /toolkits/google/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_google" 3 | version = "1.2.1" 4 | description = "Arcade.dev LLM tools for Google Workspace" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.3.0,<2.0" 10 | google-api-core = "2.19.1" 11 | google-api-python-client = "2.137.0" 12 | google-auth = "2.32.0" 13 | google-auth-httplib2 = "0.2.0" 14 | google-auth-oauthlib = "1.2.1" 15 | googleapis-common-protos = "1.63.2" 16 | beautifulsoup4 = "^4.10.0" 17 | 18 | [tool.poetry.dev-dependencies] 19 | pytest = "^8.3.0" 20 | pytest-cov = "^4.0.0" 21 | pytest-asyncio = "^0.24.0" 22 | pytest-mock = "^3.11.1" 23 | mypy = "^1.5.1" 24 | pre-commit = "^3.4.0" 25 | tox = "^4.11.1" 26 | ruff = "^0.7.4" 27 | 28 | [build-system] 29 | requires = ["poetry-core>=1.0.0,<2.0.0"] 30 | build-backend = "poetry.core.masonry.api" 31 | 32 | [tool.mypy] 33 | files = ["arcade_google/**/*.py"] 34 | python_version = "3.10" 35 | disallow_untyped_defs = "True" 36 | disallow_any_unimported = "True" 37 | no_implicit_optional = "True" 38 | check_untyped_defs = "True" 39 | warn_return_any = "True" 40 | warn_unused_ignores = "True" 41 | show_error_codes = "True" 42 | ignore_missing_imports = "True" 43 | 44 | [tool.pytest.ini_options] 45 | testpaths = ["tests"] 46 | 47 | [tool.coverage.report] 48 | skip_empty = true 49 | -------------------------------------------------------------------------------- /toolkits/google/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/google/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/google/tests/test_doc_to_markdown.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from arcade_google.doc_to_markdown import convert_document_to_markdown 4 | 5 | 6 | @pytest.mark.asyncio 7 | async def test_convert_document_to_markdown(sample_document_and_expected_formats): 8 | (sample_document, expected_markdown, _) = sample_document_and_expected_formats 9 | markdown = convert_document_to_markdown(sample_document) 10 | assert markdown == expected_markdown 11 | -------------------------------------------------------------------------------- /toolkits/hubspot/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/hubspot/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py39" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/hubspot/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Arcade 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 | -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/hubspot/arcade_hubspot/__init__.py -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | HUBSPOT_BASE_URL = "https://api.hubapi.com" 4 | HUBSPOT_CRM_BASE_URL = f"{HUBSPOT_BASE_URL}/crm" 5 | HUBSPOT_DEFAULT_API_VERSION = "v3" 6 | 7 | try: 8 | HUBSPOT_MAX_CONCURRENT_REQUESTS = int(os.getenv("HUBSPOT_MAX_CONCURRENT_REQUESTS", 3)) 9 | except ValueError: 10 | HUBSPOT_MAX_CONCURRENT_REQUESTS = 3 11 | 12 | GLOBALLY_IGNORED_FIELDS = [ 13 | "createdate", 14 | "hs_createdate", 15 | "hs_lastmodifieddate", 16 | "lastmodifieddate", 17 | ] 18 | -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/custom_critics.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from arcade.sdk.eval import BinaryCritic 4 | 5 | 6 | class ValueInListCritic(BinaryCritic): 7 | def evaluate(self, expected: list[Any], actual: Any) -> dict[str, float | bool]: 8 | match = actual in expected 9 | return {"match": match, "score": self.weight if match else 0.0} 10 | -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class HubspotObject(Enum): 5 | ACCOUNT = "account" 6 | CALL = "call" 7 | COMMUNICATION = "communication" 8 | COMPANY = "company" 9 | CONTACT = "contact" 10 | DEAL = "deal" 11 | EMAIL = "email" 12 | MEETING = "meeting" 13 | NOTE = "note" 14 | TASK = "task" 15 | 16 | @property 17 | def plural(self) -> str: 18 | if self.value == "company": 19 | return "companies" 20 | return f"{self.value}s" 21 | -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/exceptions.py: -------------------------------------------------------------------------------- 1 | from arcade.sdk.errors import ToolExecutionError 2 | 3 | 4 | class HubspotToolExecutionError(ToolExecutionError): 5 | pass 6 | 7 | 8 | class NotFoundError(HubspotToolExecutionError): 9 | pass 10 | -------------------------------------------------------------------------------- /toolkits/hubspot/arcade_hubspot/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_hubspot.tools.crm.companies import get_company_data_by_keywords 2 | from arcade_hubspot.tools.crm.contacts import create_contact, get_contact_data_by_keywords 3 | 4 | __all__ = [ 5 | "get_company_data_by_keywords", 6 | "get_contact_data_by_keywords", 7 | "create_contact", 8 | ] 9 | -------------------------------------------------------------------------------- /toolkits/hubspot/conftest.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | 3 | import pytest 4 | from arcade.sdk import ToolAuthorizationContext, ToolContext 5 | 6 | 7 | @pytest.fixture 8 | def mock_context(): 9 | mock_auth = ToolAuthorizationContext(token="fake-token") # noqa: S106 10 | return ToolContext(authorization=mock_auth) 11 | 12 | 13 | @pytest.fixture 14 | def mock_httpx_client(): 15 | with patch("arcade_hubspot.models.httpx") as mock_httpx: 16 | yield mock_httpx.AsyncClient().__aenter__.return_value 17 | -------------------------------------------------------------------------------- /toolkits/hubspot/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_hubspot" 3 | version = "0.2.0" 4 | description = "Arcade tools designed for LLMs to interact with Hubspot" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.3.2,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_hubspot/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/linkedin/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/linkedin/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/linkedin/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/linkedin/arcade_linkedin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/linkedin/arcade_linkedin/__init__.py -------------------------------------------------------------------------------- /toolkits/linkedin/arcade_linkedin/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/linkedin/arcade_linkedin/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/linkedin/arcade_linkedin/tools/constants.py: -------------------------------------------------------------------------------- 1 | LINKEDIN_BASE_URL = "https://api.linkedin.com/v2" 2 | -------------------------------------------------------------------------------- /toolkits/linkedin/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk import ToolAuthorizationContext, ToolContext 3 | 4 | 5 | @pytest.fixture 6 | def tool_context(): 7 | """Fixture for the ToolContext with mock authorization.""" 8 | return ToolContext( 9 | authorization=ToolAuthorizationContext(token="test_token", user_info={"sub": "test_user"}), # noqa: S106 10 | user_id="test_user", 11 | ) 12 | 13 | 14 | @pytest.fixture 15 | def mock_httpx_client(mocker): 16 | """Fixture to mock the httpx.AsyncClient.""" 17 | # Mock the AsyncClient context manager 18 | mock_client = mocker.patch("httpx.AsyncClient", autospec=True) 19 | async_mock_client = mock_client.return_value.__aenter__.return_value 20 | return async_mock_client 21 | -------------------------------------------------------------------------------- /toolkits/linkedin/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_linkedin" 3 | version = "0.1.10" 4 | description = "Arcade.dev LLM tools for LinkedIn" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=0.1,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_linkedin/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/linkedin/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/linkedin/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/math/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/math/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/math/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/math/arcade_math/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/math/arcade_math/__init__.py -------------------------------------------------------------------------------- /toolkits/math/arcade_math/tools/exponents.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | import math 3 | from decimal import Decimal 4 | from typing import Annotated 5 | 6 | from arcade.sdk import tool 7 | 8 | decimal.getcontext().prec = 100 9 | 10 | 11 | @tool 12 | def log( 13 | a: Annotated[str, "The number to take the logarithm of as a string"], 14 | base: Annotated[str, "The logarithmic base as a string"], 15 | ) -> Annotated[str, "The logarithm of the number with the specified base as a string"]: 16 | """ 17 | Calculate the logarithm of a number with a given base 18 | """ 19 | # Use Decimal for arbitrary precision 20 | return str(math.log(Decimal(a), Decimal(base))) 21 | 22 | 23 | @tool 24 | def power( 25 | a: Annotated[str, "The base number as a string"], 26 | b: Annotated[str, "The exponent as a string"], 27 | ) -> Annotated[str, "The result of raising a to the power of b as a string"]: 28 | """ 29 | Calculate one number raised to the power of another 30 | """ 31 | # Use Decimal for arbitrary precision 32 | return str(Decimal(a) ** Decimal(b)) 33 | -------------------------------------------------------------------------------- /toolkits/math/arcade_math/tools/miscellaneous.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | import math 3 | from decimal import Decimal 4 | from typing import Annotated 5 | 6 | from arcade.sdk import tool 7 | 8 | decimal.getcontext().prec = 100 9 | 10 | 11 | @tool 12 | def abs_val( 13 | a: Annotated[str, "The number as a string"], 14 | ) -> Annotated[str, "The absolute value of the number as a string"]: 15 | """ 16 | Calculate the absolute value of a number 17 | """ 18 | # Use Decimal for arbitrary precision 19 | return str(abs(Decimal(a))) 20 | 21 | 22 | @tool 23 | def factorial( 24 | a: Annotated[str, "The non-negative integer to compute the factorial for as a string"], 25 | ) -> Annotated[str, "The factorial of the number as a string"]: 26 | """ 27 | Compute the factorial of a non-negative integer 28 | Returns "1" for "0" 29 | """ 30 | return str(math.factorial(int(a))) 31 | 32 | 33 | @tool 34 | def sqrt( 35 | a: Annotated[str, "The number to square root as a string"], 36 | ) -> Annotated[str, "The square root of the number as a string"]: 37 | """ 38 | Get the square root of a number 39 | """ 40 | # Use Decimal for arbitrary precision 41 | a_decimal = Decimal(a) 42 | return str(a_decimal.sqrt()) 43 | -------------------------------------------------------------------------------- /toolkits/math/arcade_math/tools/rational.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import Annotated 3 | 4 | from arcade.sdk import tool 5 | 6 | 7 | @tool 8 | def gcd( 9 | a: Annotated[str, "First integer as a string"], 10 | b: Annotated[str, "Second integer as a string"], 11 | ) -> Annotated[str, "The greatest common divisor of a and b as a string"]: 12 | """ 13 | Calculate the greatest common divisor (GCD) of two integers. 14 | """ 15 | return str(math.gcd(int(a), int(b))) 16 | 17 | 18 | @tool 19 | def lcm( 20 | a: Annotated[str, "First integer as a string"], 21 | b: Annotated[str, "Second integer as a string"], 22 | ) -> Annotated[str, "The least common multiple of a and b as a string"]: 23 | """ 24 | Calculate the least common multiple (LCM) of two integers. 25 | Returns "0" if either integer is 0. 26 | """ 27 | a_int, b_int = int(a), int(b) 28 | if a_int == 0 or b_int == 0: 29 | return "0" 30 | return str(abs(a_int * b_int) // math.gcd(a_int, b_int)) 31 | -------------------------------------------------------------------------------- /toolkits/math/arcade_math/tools/statistics.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | from decimal import Decimal 3 | from statistics import median as stats_median 4 | from typing import Annotated 5 | 6 | from arcade.sdk import tool 7 | 8 | decimal.getcontext().prec = 100 9 | 10 | 11 | @tool 12 | def avg( 13 | numbers: Annotated[list[str], "The list of numbers as strings"], 14 | ) -> Annotated[str, "The average (mean) of the numbers in the list as a string"]: 15 | """ 16 | Calculate the average (mean) of a list of numbers. 17 | Returns "0.0" if the list is empty. 18 | """ 19 | # Use Decimal for arbitrary precision 20 | d_numbers = [Decimal(n) for n in numbers] 21 | return str(sum(d_numbers) / len(d_numbers)) if d_numbers else "0.0" 22 | 23 | 24 | @tool 25 | def median( 26 | numbers: Annotated[list[str], "A list of numbers as strings"], 27 | ) -> Annotated[str, "The median value of the numbers in the list as a string"]: 28 | """ 29 | Calculate the median of a list of numbers. 30 | Returns "0.0" if the list is empty. 31 | """ 32 | # Use Decimal for arbitrary precision 33 | d_numbers = [Decimal(n) for n in numbers] 34 | return str(stats_median(d_numbers)) if d_numbers else "0.0" 35 | -------------------------------------------------------------------------------- /toolkits/math/arcade_math/tools/trigonometry.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | import math 3 | from decimal import Decimal 4 | from typing import Annotated 5 | 6 | from arcade.sdk import tool 7 | 8 | decimal.getcontext().prec = 100 9 | 10 | 11 | @tool 12 | def deg_to_rad( 13 | degrees: Annotated[str, "Angle in degrees as a string"], 14 | ) -> Annotated[str, "Angle in radians as a string"]: 15 | """ 16 | Convert an angle from degrees to radians. 17 | """ 18 | # Use Decimal for arbitrary precision 19 | return str(math.radians(Decimal(degrees))) 20 | 21 | 22 | @tool 23 | def rad_to_deg( 24 | radians: Annotated[str, "Angle in radians as a string"], 25 | ) -> Annotated[str, "Angle in degrees as a string"]: 26 | """ 27 | Convert an angle from radians to degrees. 28 | """ 29 | # Use Decimal for arbitrary precision 30 | return str(math.degrees(Decimal(radians))) 31 | -------------------------------------------------------------------------------- /toolkits/math/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_math" 3 | version = "1.0.1" 4 | description = "Arcade.dev LLM tools for doing math" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | 11 | [tool.poetry.dev-dependencies] 12 | pytest = "^8.3.0" 13 | pytest-cov = "^4.0.0" 14 | pytest-asyncio = "^0.24.0" 15 | pytest-mock = "^3.11.1" 16 | mypy = "^1.5.1" 17 | pre-commit = "^3.4.0" 18 | tox = "^4.11.1" 19 | ruff = "^0.7.4" 20 | 21 | [build-system] 22 | requires = ["poetry-core>=1.0.0,<2.0.0"] 23 | build-backend = "poetry.core.masonry.api" 24 | 25 | [tool.mypy] 26 | files = ["arcade_math/**/*.py"] 27 | python_version = "3.10" 28 | disallow_untyped_defs = "True" 29 | disallow_any_unimported = "True" 30 | no_implicit_optional = "True" 31 | check_untyped_defs = "True" 32 | warn_return_any = "True" 33 | warn_unused_ignores = "True" 34 | show_error_codes = "True" 35 | ignore_missing_imports = "True" 36 | 37 | [tool.pytest.ini_options] 38 | testpaths = ["tests"] 39 | 40 | [tool.coverage.report] 41 | skip_empty = true 42 | -------------------------------------------------------------------------------- /toolkits/math/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/math/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/math/tests/test_rational.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk.errors import ToolExecutionError 3 | 4 | from arcade_math.tools.rational import ( 5 | gcd, 6 | lcm, 7 | ) 8 | 9 | 10 | def test_gcd(): 11 | assert gcd("-15", "-5") == "5" 12 | assert gcd("15", "0") == "15" 13 | assert gcd("15", "-2") == "1" 14 | assert gcd("15", "-0") == "15" 15 | assert gcd("15", "5") == "5" 16 | assert gcd("7", "13") == "1" 17 | assert gcd("-13", "13") == "13" 18 | with pytest.raises(ToolExecutionError): 19 | gcd("15.0", "5.0") 20 | 21 | 22 | def test_lcm(): 23 | assert lcm("-15", "-5") == "15" 24 | assert lcm("15", "0") == "0" 25 | assert lcm("15", "-2") == "30" 26 | assert lcm("15", "-0") == "0" 27 | assert lcm("15", "5") == "15" 28 | assert lcm("7", "13") == "91" 29 | assert lcm("-13", "13") == "13" 30 | with pytest.raises(ToolExecutionError): 31 | lcm("15.0", "5.0") 32 | -------------------------------------------------------------------------------- /toolkits/math/tests/test_statistics.py: -------------------------------------------------------------------------------- 1 | from arcade_math.tools.statistics import ( 2 | avg, 3 | median, 4 | ) 5 | 6 | 7 | def test_avg(): 8 | assert avg(["1", "2", "3", "4", "5", "6"]) == "3.5" 9 | assert avg([]) == "0.0" 10 | assert avg(["-1", "-2", "-3", "-4", "-5", "-6"]) == "-3.5" 11 | assert avg(["0.1", "0.2", "0.3", "0.3", "0.5", "0.7"]) == "0.35" 12 | 13 | 14 | def test_median(): 15 | assert median(["1", "2", "3", "4", "5", "6"]) == "3.5" 16 | assert median([]) == "0.0" 17 | assert median(["-1", "-2", "-3", "-4", "-5", "-6"]) == "-3.5" 18 | assert median(["0.1", "0.2", "0.3", "0.3", "0.5", "0.7"]) == "0.3" 19 | -------------------------------------------------------------------------------- /toolkits/microsoft/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/microsoft/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | -------------------------------------------------------------------------------- /toolkits/microsoft/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade 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 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/microsoft/arcade_microsoft/__init__.py -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/client.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import Any 3 | 4 | from azure.core.credentials import AccessToken, TokenCredential 5 | from msgraph import GraphServiceClient 6 | 7 | DEFAULT_SCOPE = "https://graph.microsoft.com/.default" 8 | 9 | 10 | class StaticTokenCredential(TokenCredential): 11 | """Implementation of TokenCredential protocol to be provided to the MSGraph SDK client""" 12 | 13 | def __init__(self, token: str): 14 | self._token = token 15 | 16 | def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: 17 | # An expiration is required by MSGraph SDK. Set to 1 hour from now. 18 | expires_on = int(datetime.datetime.now(datetime.timezone.utc).timestamp()) + 3600 19 | return AccessToken(self._token, expires_on) 20 | 21 | 22 | def get_client(token: str) -> GraphServiceClient: 23 | """Create and return a MSGraph SDK client, given the provided token.""" 24 | token_credential = StaticTokenCredential(token) 25 | 26 | return GraphServiceClient(token_credential, scopes=[DEFAULT_SCOPE]) 27 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_calendar/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_microsoft.outlook_calendar.tools import ( 2 | create_event, 3 | get_event, 4 | list_events_in_time_range, 5 | ) 6 | 7 | __all__ = ["create_event", "get_event", "list_events_in_time_range"] 8 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_calendar/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_microsoft.outlook_calendar.tools.create_event import create_event 2 | from arcade_microsoft.outlook_calendar.tools.get_event import get_event 3 | from arcade_microsoft.outlook_calendar.tools.list_events_in_time_range import ( 4 | list_events_in_time_range, 5 | ) 6 | 7 | __all__ = ["create_event", "get_event", "list_events_in_time_range"] 8 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_calendar/tools/get_event.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from arcade.sdk import ToolContext, tool 4 | from arcade.sdk.auth import Microsoft 5 | 6 | from arcade_microsoft.client import get_client 7 | from arcade_microsoft.outlook_calendar._utils import ( 8 | create_timezone_request_config, 9 | get_default_calendar_timezone, 10 | ) 11 | from arcade_microsoft.outlook_calendar.models import Event 12 | 13 | 14 | @tool(requires_auth=Microsoft(scopes=["MailboxSettings.Read", "Calendars.ReadBasic"])) 15 | async def get_event( 16 | context: ToolContext, 17 | event_id: Annotated[str, "The ID of the event to get"], 18 | ) -> Annotated[dict, "A dictionary containing the event details"]: 19 | """Get an event by its ID from the user's calendar.""" 20 | client = get_client(context.get_auth_token_or_empty()) 21 | 22 | time_zone = await get_default_calendar_timezone(client) 23 | request_config = create_timezone_request_config(time_zone) 24 | 25 | response = await client.me.events.by_event_id(event_id).get( 26 | request_configuration=request_config 27 | ) 28 | 29 | return Event.from_sdk(response).to_dict() # type: ignore[arg-type] 30 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_mail/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_microsoft.outlook_mail.tools import ( 2 | create_and_send_email, 3 | create_draft_email, 4 | list_emails, 5 | list_emails_by_property, 6 | list_emails_in_folder, 7 | reply_to_email, 8 | send_draft_email, 9 | update_draft_email, 10 | ) 11 | 12 | __all__ = [ 13 | # Read 14 | "list_emails", 15 | "list_emails_by_property", 16 | "list_emails_in_folder", 17 | # Send 18 | "create_and_send_email", 19 | "send_draft_email", 20 | "reply_to_email", 21 | # Write 22 | "create_draft_email", 23 | "update_draft_email", 24 | ] 25 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_mail/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_MESSAGE_FIELDS = [ 2 | "bccRecipients", 3 | "body", 4 | "ccRecipients", 5 | "conversationId", 6 | "conversationIndex", 7 | "flag", 8 | "from", 9 | "hasAttachments", 10 | "importance", 11 | "isDraft", 12 | "isRead", 13 | "receivedDateTime", 14 | "replyTo", 15 | "subject", 16 | "toRecipients", 17 | "webLink", 18 | ] 19 | -------------------------------------------------------------------------------- /toolkits/microsoft/arcade_microsoft/outlook_mail/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_microsoft.outlook_mail.tools.read import ( 2 | list_emails, 3 | list_emails_by_property, 4 | list_emails_in_folder, 5 | ) 6 | from arcade_microsoft.outlook_mail.tools.send import ( 7 | create_and_send_email, 8 | reply_to_email, 9 | send_draft_email, 10 | ) 11 | from arcade_microsoft.outlook_mail.tools.write import ( 12 | create_draft_email, 13 | update_draft_email, 14 | ) 15 | 16 | __all__ = [ 17 | # Read 18 | "list_emails", 19 | "list_emails_by_property", 20 | "list_emails_in_folder", 21 | # Send 22 | "create_and_send_email", 23 | "reply_to_email", 24 | "send_draft_email", 25 | # Write 26 | "create_draft_email", 27 | "update_draft_email", 28 | ] 29 | -------------------------------------------------------------------------------- /toolkits/microsoft/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_microsoft" 3 | version = "0.2.0" 4 | description = "Arcade.dev LLM tools for Outlook Mail" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = "^1.3.2" 10 | msgraph-sdk = "^1.28.0" 11 | beautifulsoup4 = "^4.10.0" 12 | pytz = "^2024.2" 13 | 14 | [tool.poetry.dev-dependencies] 15 | pytest = "^8.3.0" 16 | pytest-cov = "^4.0.0" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_microsoft/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/microsoft/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/microsoft/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/microsoft/tests/outlook_calendar/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/microsoft/tests/outlook_calendar/__init__.py -------------------------------------------------------------------------------- /toolkits/microsoft/tests/outlook_mail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/microsoft/tests/outlook_mail/__init__.py -------------------------------------------------------------------------------- /toolkits/notion/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/notion/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | -------------------------------------------------------------------------------- /toolkits/notion/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/notion/arcade_notion_toolkit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/notion/arcade_notion_toolkit/__init__.py -------------------------------------------------------------------------------- /toolkits/notion/arcade_notion_toolkit/constants.py: -------------------------------------------------------------------------------- 1 | NOTION_API_URL = "https://api.notion.com/v1" 2 | 3 | 4 | ENDPOINTS = { 5 | "create_a_page": "/pages", 6 | "retrieve_block_children": "/blocks/{block_id}/children", 7 | "search_by_title": "/search", 8 | "query_a_database": "/databases/{database_id}/query", 9 | "update_page_properties": "/pages/{page_id}", 10 | "append_block_children": "/blocks/{block_id}/children", 11 | "retrieve_a_database": "/databases/{database_id}", 12 | "create_comment": "/comments", 13 | "retrieve_a_page": "/pages/{page_id}", 14 | "retrieve_a_block": "/blocks/{block_id}", 15 | } 16 | 17 | UNTITLED_TITLE = "New Page" 18 | -------------------------------------------------------------------------------- /toolkits/notion/arcade_notion_toolkit/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class SortDirection(str, Enum): 5 | ASCENDING = "ascending" 6 | DESCENDING = "descending" 7 | 8 | 9 | class ObjectType(str, Enum): 10 | PAGE = "page" 11 | DATABASE = "database" 12 | 13 | 14 | class BlockType(str, Enum): 15 | BOOKMARK = "bookmark" 16 | BREADCRUMB = "breadcrumb" 17 | BULLETED_LIST_ITEM = "bulleted_list_item" 18 | CALLOUT = "callout" 19 | CHILD_DATABASE = "child_database" 20 | CHILD_PAGE = "child_page" 21 | COLUMN = "column" 22 | COLUMN_LIST = "column_list" 23 | DIVIDER = "divider" 24 | EMBED = "embed" 25 | EQUATION = "equation" 26 | FILE = "file" 27 | HEADING_1 = "heading_1" 28 | HEADING_2 = "heading_2" 29 | HEADING_3 = "heading_3" 30 | IMAGE = "image" 31 | LINK_PREVIEW = "link_preview" 32 | LINK_TO_PAGE = "link_to_page" 33 | NUMBERED_LIST_ITEM = "numbered_list_item" 34 | PARAGRAPH = "paragraph" 35 | PDF = "pdf" 36 | QUOTE = "quote" 37 | SYNCED_BLOCK = "synced_block" 38 | TABLE = "table" 39 | TABLE_OF_CONTENTS = "table_of_contents" 40 | TABLE_ROW = "table_row" 41 | TEMPLATE = "template" 42 | TO_DO = "to_do" 43 | TOGGLE = "toggle" 44 | UNSUPPORTED = "unsupported" 45 | VIDEO = "video" 46 | -------------------------------------------------------------------------------- /toolkits/notion/arcade_notion_toolkit/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_notion_toolkit.tools.pages import ( 2 | create_page, 3 | get_page_content_by_id, 4 | get_page_content_by_title, 5 | ) 6 | from arcade_notion_toolkit.tools.search import ( 7 | get_object_metadata, 8 | get_workspace_structure, 9 | search_by_title, 10 | ) 11 | 12 | __all__ = [ 13 | "create_page", 14 | "get_object_metadata", 15 | "get_page_content_by_id", 16 | "get_page_content_by_title", 17 | "search_by_title", 18 | "get_workspace_structure", 19 | ] 20 | -------------------------------------------------------------------------------- /toolkits/notion/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk import ToolAuthorizationContext, ToolContext 3 | 4 | 5 | @pytest.fixture 6 | def mock_context(): 7 | mock_auth = ToolAuthorizationContext(token="fake-token") # noqa: S106 8 | return ToolContext(authorization=mock_auth) 9 | -------------------------------------------------------------------------------- /toolkits/notion/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_notion_toolkit" 3 | version = "0.1.3" 4 | description = "Arcade.dev LLM tools for Notion" 5 | authors = ["ArcadeAI "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = "^1.1.2" 10 | 11 | [tool.poetry.dev-dependencies] 12 | pytest = "^8.3.0" 13 | pytest-cov = "^4.0.0" 14 | pytest-asyncio = "^0.24.0" 15 | mypy = "^1.5.1" 16 | pre-commit = "^3.4.0" 17 | tox = "^4.11.1" 18 | ruff = "^0.7.4" 19 | 20 | [build-system] 21 | requires = ["poetry-core>=1.0.0,<2.0.0"] 22 | build-backend = "poetry.core.masonry.api" 23 | 24 | [tool.mypy] 25 | files = ["arcade_notion_toolkit/**/*.py"] 26 | python_version = "3.10" 27 | disallow_untyped_defs = "True" 28 | disallow_any_unimported = "True" 29 | no_implicit_optional = "True" 30 | check_untyped_defs = "True" 31 | warn_return_any = "True" 32 | warn_unused_ignores = "True" 33 | show_error_codes = "True" 34 | ignore_missing_imports = "True" 35 | 36 | [tool.pytest.ini_options] 37 | testpaths = ["tests"] 38 | 39 | [tool.coverage.report] 40 | skip_empty = true 41 | -------------------------------------------------------------------------------- /toolkits/notion/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/notion/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/notion/tests/test_tools_search.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.asyncio 5 | async def test_search_by_title() -> None: 6 | pass 7 | -------------------------------------------------------------------------------- /toolkits/reddit/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/reddit/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | -------------------------------------------------------------------------------- /toolkits/reddit/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade 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 | -------------------------------------------------------------------------------- /toolkits/reddit/arcade_reddit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/reddit/arcade_reddit/__init__.py -------------------------------------------------------------------------------- /toolkits/reddit/arcade_reddit/client.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | import httpx 4 | 5 | 6 | class RedditClient: 7 | BASE_URL = "https://oauth.reddit.com/" 8 | 9 | def __init__(self, token: str): 10 | self.token = token 11 | 12 | async def request(self, method: str, path: str, **kwargs: Any) -> Any: 13 | headers = { 14 | "Authorization": f"Bearer {self.token}", 15 | "User-Agent": "arcade-reddit", 16 | } 17 | async with httpx.AsyncClient() as client: 18 | response = await client.request( 19 | method, f"{self.BASE_URL}/{path.lstrip('/')}", headers=headers, **kwargs 20 | ) 21 | response.raise_for_status() 22 | return response.json() 23 | 24 | async def get(self, path: str, **kwargs: Any) -> Any: 25 | return await self.request("GET", path, **kwargs) 26 | 27 | async def post(self, path: str, **kwargs: Any) -> Any: 28 | return await self.request("POST", path, **kwargs) 29 | -------------------------------------------------------------------------------- /toolkits/reddit/arcade_reddit/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_reddit.tools.read import ( 2 | check_subreddit_access, 3 | get_content_of_multiple_posts, 4 | get_content_of_post, 5 | get_my_posts, 6 | get_my_username, 7 | get_posts_in_subreddit, 8 | get_subreddit_rules, 9 | get_top_level_comments, 10 | ) 11 | from arcade_reddit.tools.submit import ( 12 | comment_on_post, 13 | reply_to_comment, 14 | submit_text_post, 15 | ) 16 | 17 | __all__ = [ 18 | "check_subreddit_access", 19 | "comment_on_post", 20 | "get_content_of_multiple_posts", 21 | "get_content_of_post", 22 | "get_my_posts", 23 | "get_my_username", 24 | "get_posts_in_subreddit", 25 | "get_subreddit_rules", 26 | "get_top_level_comments", 27 | "reply_to_comment", 28 | "submit_text_post", 29 | ] 30 | -------------------------------------------------------------------------------- /toolkits/reddit/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_reddit" 3 | version = "0.1.0" 4 | description = "Arcade.dev LLM tools Reddit" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = "^1.0.5" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | mypy = "^1.5.1" 17 | pre-commit = "^3.4.0" 18 | tox = "^4.11.1" 19 | ruff = "^0.7.4" 20 | 21 | [build-system] 22 | requires = ["poetry-core>=1.0.0,<2.0.0"] 23 | build-backend = "poetry.core.masonry.api" 24 | 25 | [tool.mypy] 26 | files = ["arcade_reddit/**/*.py"] 27 | python_version = "3.10" 28 | disallow_untyped_defs = "True" 29 | disallow_any_unimported = "True" 30 | no_implicit_optional = "True" 31 | check_untyped_defs = "True" 32 | warn_return_any = "True" 33 | warn_unused_ignores = "True" 34 | show_error_codes = "True" 35 | ignore_missing_imports = "True" 36 | 37 | [tool.pytest.ini_options] 38 | testpaths = ["tests"] 39 | 40 | [tool.coverage.report] 41 | skip_empty = true 42 | -------------------------------------------------------------------------------- /toolkits/reddit/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/reddit/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/salesforce/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/salesforce/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/salesforce/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Arcade 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 | -------------------------------------------------------------------------------- /toolkits/salesforce/arcade_salesforce/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/salesforce/arcade_salesforce/__init__.py -------------------------------------------------------------------------------- /toolkits/salesforce/arcade_salesforce/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | SALESFORCE_API_VERSION = "v63.0" 4 | 5 | DEFAULT_MAX_CONCURRENT_REQUESTS = 3 6 | try: 7 | MAX_CONCURRENT_REQUESTS = int( 8 | os.getenv("ARCADE_SALESFORCE_MAX_CONCURRENT_REQUESTS", DEFAULT_MAX_CONCURRENT_REQUESTS) 9 | ) 10 | except ValueError: 11 | MAX_CONCURRENT_REQUESTS = DEFAULT_MAX_CONCURRENT_REQUESTS 12 | 13 | ASSOCIATION_REFERENCE_FIELDS = [ 14 | "AccountId", 15 | "OwnerId", 16 | "AssociatedToWhom", 17 | "ContactId", 18 | ] 19 | 20 | GLOBALLY_IGNORED_FIELDS = [ 21 | "attributes", 22 | "CleanStatus", 23 | "CreatedById", 24 | "CreatedDate", 25 | "IsDeleted", 26 | "LastModifiedById", 27 | "LastModifiedDate", 28 | "LastReferencedDate", 29 | "LastViewedDate", 30 | "PhotoUrl", 31 | "SystemModstamp", 32 | "WhatId", 33 | ] 34 | -------------------------------------------------------------------------------- /toolkits/salesforce/arcade_salesforce/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import cast 3 | 4 | 5 | class SalesforceObject(Enum): 6 | ACCOUNT = "Account" 7 | CALL = "Call" 8 | CONTACT = "Contact" 9 | EMAIL = "Email" 10 | EVENT = "Event" 11 | LEAD = "Lead" 12 | NOTE = "Note" 13 | OPPORTUNITY = "Opportunity" 14 | TASK = "Task" 15 | USER = "User" 16 | 17 | @property 18 | def plural(self) -> str: 19 | if self == SalesforceObject.OPPORTUNITY: 20 | return "Opportunities" 21 | return cast(str, self.value) + "s" 22 | -------------------------------------------------------------------------------- /toolkits/salesforce/arcade_salesforce/exceptions.py: -------------------------------------------------------------------------------- 1 | from arcade.sdk.errors import ToolExecutionError 2 | 3 | 4 | class SalesforceToolExecutionError(ToolExecutionError): 5 | def __init__(self, errors: list[str], message: str = "Failed to execute Salesforce tool"): 6 | self.message = message 7 | self.errors = errors 8 | exc_message = f"{message}. Errors: {errors}" 9 | super().__init__( 10 | message=exc_message, 11 | developer_message=exc_message, 12 | ) 13 | 14 | 15 | class ResourceNotFoundError(SalesforceToolExecutionError): 16 | def __init__(self, errors: list[str]): 17 | super().__init__(message="Resource not found", errors=errors) 18 | 19 | 20 | class BadRequestError(SalesforceToolExecutionError): 21 | def __init__(self, errors: list[str]): 22 | super().__init__(message="Bad request", errors=errors) 23 | -------------------------------------------------------------------------------- /toolkits/salesforce/arcade_salesforce/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_salesforce.tools.crm.account import ( 2 | get_account_data_by_id, 3 | get_account_data_by_keywords, 4 | ) 5 | from arcade_salesforce.tools.crm.contact import create_contact 6 | 7 | __all__ = [ 8 | "create_contact", 9 | "get_account_data_by_id", 10 | "get_account_data_by_keywords", 11 | ] 12 | -------------------------------------------------------------------------------- /toolkits/salesforce/conftest.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | 3 | import pytest 4 | from arcade.sdk import ToolAuthorizationContext, ToolContext 5 | 6 | 7 | @pytest.fixture 8 | def mock_context(): 9 | mock_auth = ToolAuthorizationContext(token="fake-token") # noqa: S106 10 | return ToolContext(authorization=mock_auth) 11 | 12 | 13 | @pytest.fixture 14 | def mock_httpx_client(): 15 | with patch("arcade_salesforce.models.httpx") as mock_httpx: 16 | yield mock_httpx.AsyncClient().__aenter__.return_value 17 | -------------------------------------------------------------------------------- /toolkits/salesforce/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_salesforce" 3 | version = "0.1.0" 4 | description = "Arcade tools designed for LLMs to interact with Salesforce" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_salesforce/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/salesforce/tests/test_create_contact.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/salesforce/tests/test_create_contact.py -------------------------------------------------------------------------------- /toolkits/salesforce/tests/test_get_account_data.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/salesforce/tests/test_get_account_data.py -------------------------------------------------------------------------------- /toolkits/search/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/search/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/search/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/search/arcade_search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/search/arcade_search/__init__.py -------------------------------------------------------------------------------- /toolkits/search/arcade_search/exceptions.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from arcade.sdk.errors import RetryableToolError 4 | 5 | from arcade_search.google_data import COUNTRY_CODES, LANGUAGE_CODES 6 | 7 | 8 | class GoogleRetryableError(RetryableToolError): 9 | pass 10 | 11 | 12 | class CountryNotFoundError(GoogleRetryableError): 13 | def __init__(self, country: str | None) -> None: 14 | valid_countries = json.dumps(COUNTRY_CODES, default=str) 15 | message = f"Country not found: '{country}'." 16 | additional_message = f"Valid countries are: {valid_countries}" 17 | super().__init__(message, additional_prompt_content=additional_message) 18 | 19 | 20 | class LanguageNotFoundError(GoogleRetryableError): 21 | def __init__(self, language: str | None) -> None: 22 | valid_languages = json.dumps(LANGUAGE_CODES, default=str) 23 | message = f"Language not found: '{language}'." 24 | additional_message = f"Valid languages are: {valid_languages}" 25 | super().__init__(message, additional_prompt_content=additional_message) 26 | -------------------------------------------------------------------------------- /toolkits/search/arcade_search/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_search.tools.google_finance import get_stock_historical_data, get_stock_summary 2 | from arcade_search.tools.google_flights import search_one_way_flights, search_roundtrip_flights 3 | from arcade_search.tools.google_hotels import search_hotels 4 | from arcade_search.tools.google_jobs import search_jobs 5 | from arcade_search.tools.google_maps import ( 6 | get_directions_between_addresses, 7 | get_directions_between_coordinates, 8 | ) 9 | from arcade_search.tools.google_search import search_google 10 | 11 | __all__ = [ 12 | "search_google", # Google Search 13 | "get_stock_summary", # Google Finance 14 | "get_stock_historical_data", # Google Finance 15 | "search_one_way_flights", # Google Flights 16 | "search_roundtrip_flights", # Google Flights 17 | "search_hotels", # Google Hotels 18 | "get_directions_between_addresses", # Google Maps 19 | "get_directions_between_coordinates", # Google Maps 20 | "search_jobs", # Google Jobs 21 | ] 22 | -------------------------------------------------------------------------------- /toolkits/search/arcade_search/tools/google_search.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Annotated 3 | 4 | from arcade.sdk import ToolContext, tool 5 | 6 | from arcade_search.utils import call_serpapi, prepare_params 7 | 8 | 9 | @tool(requires_secrets=["SERP_API_KEY"]) 10 | async def search_google( 11 | context: ToolContext, 12 | query: Annotated[str, "Search query"], 13 | n_results: Annotated[int, "Number of results to retrieve"] = 5, 14 | ) -> str: 15 | """Search Google using SerpAPI and return organic search results.""" 16 | 17 | params = prepare_params("google", q=query) 18 | results = call_serpapi(context, params) 19 | organic_results = results.get("organic_results", []) 20 | 21 | return json.dumps(organic_results[:n_results]) 22 | -------------------------------------------------------------------------------- /toolkits/search/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | class DummyContext: 5 | def get_secret(self, key: str) -> str | None: 6 | if key.lower() == "serp_api_key": 7 | return "dummy_key" 8 | return None 9 | 10 | 11 | @pytest.fixture 12 | def dummy_context(): 13 | return DummyContext() 14 | -------------------------------------------------------------------------------- /toolkits/search/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_search" 3 | version = "1.4.0" 4 | description = "Arcade.dev LLM tools for searching the web" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | serpapi = "^0.1.5" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_search/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/search/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/search/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/slack/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/slack/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/slack/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/slack/arcade_slack/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/slack/arcade_slack/__init__.py -------------------------------------------------------------------------------- /toolkits/slack/arcade_slack/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from arcade_slack.custom_types import PositiveInt 4 | 5 | MAX_PAGINATION_SIZE_LIMIT = 200 6 | 7 | MAX_PAGINATION_TIMEOUT_SECONDS = PositiveInt( 8 | os.environ.get( 9 | "MAX_PAGINATION_TIMEOUT_SECONDS", 10 | os.environ.get("MAX_SLACK_PAGINATION_TIMEOUT_SECONDS", 30), 11 | ), 12 | name="MAX_PAGINATION_TIMEOUT_SECONDS or MAX_SLACK_PAGINATION_TIMEOUT_SECONDS", 13 | ) 14 | -------------------------------------------------------------------------------- /toolkits/slack/arcade_slack/custom_types.py: -------------------------------------------------------------------------------- 1 | from typing import NewType 2 | 3 | 4 | class PositiveInt(int): 5 | def __new__(cls, value: str | int, name: str = "value") -> "PositiveInt": 6 | def validate(val: int) -> int: 7 | if val <= 0: 8 | raise ValueError(f"{name} must be positive, got {val}") 9 | return val 10 | 11 | try: 12 | value = int(value) 13 | except ValueError: 14 | raise ValueError(f"{name} must be a valid integer, got {value!r}") 15 | 16 | validated_value = validate(value) 17 | instance = super().__new__(cls, validated_value) 18 | return instance 19 | 20 | 21 | SlackOffsetSecondsFromUTC = NewType("SlackOffsetSecondsFromUTC", int) # observe it can be negative 22 | SlackPaginationNextCursor = str | None 23 | SlackUserFieldId = NewType("SlackUserFieldId", str) 24 | SlackUserId = NewType("SlackUserId", str) 25 | SlackTeamId = NewType("SlackTeamId", str) 26 | SlackTimestampStr = NewType("SlackTimestampStr", str) 27 | -------------------------------------------------------------------------------- /toolkits/slack/arcade_slack/exceptions.py: -------------------------------------------------------------------------------- 1 | class SlackToolkitError(Exception): 2 | """Base class for all Slack toolkit errors.""" 3 | 4 | 5 | class PaginationTimeoutError(SlackToolkitError): 6 | """Raised when a timeout occurs during pagination.""" 7 | 8 | def __init__(self, timeout_seconds: int): 9 | self.timeout_seconds = timeout_seconds 10 | super().__init__(f"The pagination process timed out after {timeout_seconds} seconds.") 11 | 12 | 13 | class ItemNotFoundError(SlackToolkitError): 14 | """Raised when an item is not found.""" 15 | 16 | 17 | class UsernameNotFoundError(SlackToolkitError): 18 | """Raised when a user is not found by the username searched""" 19 | 20 | def __init__(self, usernames_found: list[str], username_not_found: str) -> None: 21 | self.usernames_found = usernames_found 22 | self.username_not_found = username_not_found 23 | 24 | 25 | class ConversationNotFoundError(SlackToolkitError): 26 | """Raised when a conversation is not found""" 27 | 28 | 29 | class DirectMessageConversationNotFoundError(ConversationNotFoundError): 30 | """Raised when a direct message conversation searched is not found""" 31 | -------------------------------------------------------------------------------- /toolkits/slack/arcade_slack/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/slack/arcade_slack/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/slack/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk import ToolAuthorizationContext, ToolContext 3 | 4 | 5 | @pytest.fixture 6 | def mock_context(): 7 | mock_auth = ToolAuthorizationContext(token="fake-token") # noqa: S106 8 | return ToolContext(authorization=mock_auth) 9 | 10 | 11 | @pytest.fixture 12 | def mock_chat_slack_client(mocker): 13 | mock_client = mocker.patch("arcade_slack.tools.chat.AsyncWebClient", autospec=True) 14 | return mock_client.return_value 15 | 16 | 17 | @pytest.fixture 18 | def mock_users_slack_client(mocker): 19 | mock_client = mocker.patch("arcade_slack.tools.users.AsyncWebClient", autospec=True) 20 | return mock_client.return_value 21 | -------------------------------------------------------------------------------- /toolkits/slack/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/slack/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/spotify/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/spotify/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/spotify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/spotify/arcade_spotify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/spotify/arcade_spotify/__init__.py -------------------------------------------------------------------------------- /toolkits/spotify/arcade_spotify/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/spotify/arcade_spotify/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/spotify/arcade_spotify/tools/tracks.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from arcade.sdk import ToolContext, tool 4 | from arcade.sdk.auth import Spotify 5 | 6 | from arcade_spotify.tools.utils import ( 7 | get_url, 8 | send_spotify_request, 9 | ) 10 | 11 | 12 | @tool(requires_auth=Spotify()) 13 | async def get_track_from_id( 14 | context: ToolContext, 15 | track_id: Annotated[str, "The Spotify ID of the track"], 16 | ) -> Annotated[dict, "Information about the track"]: 17 | """Get information about a track""" 18 | url = get_url("tracks_get_track", track_id=track_id) 19 | 20 | response = await send_spotify_request(context, "GET", url) 21 | response.raise_for_status() 22 | return dict(response.json()) 23 | -------------------------------------------------------------------------------- /toolkits/spotify/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk import ToolContext 3 | 4 | 5 | @pytest.fixture 6 | def tool_context(): 7 | """Fixture for the ToolContext with mock authorization.""" 8 | return ToolContext(authorization={"token": "test_token", "user_id": "test_user"}) 9 | 10 | 11 | @pytest.fixture 12 | def mock_httpx_client(mocker): 13 | """Fixture to mock the httpx.AsyncClient.""" 14 | # Mock the AsyncClient context manager 15 | mock_client = mocker.patch("httpx.AsyncClient", autospec=True) 16 | async_mock_client = mock_client.return_value.__aenter__.return_value 17 | return async_mock_client 18 | 19 | 20 | @pytest.fixture 21 | def sample_track(): 22 | """Fixture for a sample track.""" 23 | return { 24 | "album": {"id": "1234567890", "name": "Test Album", "uri": "spotify:album:1234567890"}, 25 | "artists": [{"name": "Test Artist", "type": "artist", "uri": "spotify:artist:1234567890"}], 26 | "available_markets": ["us"], 27 | "duration_ms": 123456, 28 | "id": "1234567890", 29 | "is_playable": True, 30 | "name": "Test Track", 31 | "popularity": 100, 32 | "type": "track", 33 | "uri": "spotify:track:1234567890", 34 | } 35 | -------------------------------------------------------------------------------- /toolkits/spotify/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_spotify" 3 | version = "0.2.1" 4 | description = "Arcade.dev LLM tools for Spotify" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=0.1,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_spotify/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/spotify/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/spotify/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/stripe/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/stripe/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "**/tests/*" = ["S101"] 41 | 42 | [format] 43 | preview = true 44 | skip-magic-trailing-comma = false 45 | -------------------------------------------------------------------------------- /toolkits/stripe/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade 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 | -------------------------------------------------------------------------------- /toolkits/stripe/arcade_stripe/__init__.py: -------------------------------------------------------------------------------- 1 | from arcade_stripe.tools.stripe import ( 2 | create_billing_portal_session, 3 | create_customer, 4 | create_invoice, 5 | create_invoice_item, 6 | create_payment_link, 7 | create_price, 8 | create_product, 9 | create_refund, 10 | finalize_invoice, 11 | list_customers, 12 | list_invoices, 13 | list_payment_intents, 14 | list_prices, 15 | list_products, 16 | retrieve_balance, 17 | ) 18 | 19 | __all__ = [ 20 | "create_billing_portal_session", 21 | "create_customer", 22 | "create_invoice", 23 | "create_invoice_item", 24 | "create_payment_link", 25 | "create_price", 26 | "create_product", 27 | "create_refund", 28 | "finalize_invoice", 29 | "list_customers", 30 | "list_invoices", 31 | "list_payment_intents", 32 | "list_prices", 33 | "list_products", 34 | "retrieve_balance", 35 | ] 36 | -------------------------------------------------------------------------------- /toolkits/stripe/arcade_stripe/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/stripe/arcade_stripe/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/stripe/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_stripe" 3 | version = "0.0.1" 4 | description = "Arcade.dev LLM tools for Stripe" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.11" 9 | arcade-ai = "^1.0.5" 10 | stripe-agent-toolkit = "^0.6.1" 11 | stripe = "^11.0.0" 12 | 13 | [tool.poetry.dev-dependencies] 14 | pytest = "^8.3.0" 15 | pytest-cov = "^4.0.0" 16 | mypy = "^1.5.1" 17 | pre-commit = "^3.4.0" 18 | tox = "^4.11.1" 19 | ruff = "^0.7.4" 20 | 21 | [build-system] 22 | requires = ["poetry-core>=1.0.0,<2.0.0"] 23 | build-backend = "poetry.core.masonry.api" 24 | 25 | [tool.mypy] 26 | files = ["arcade_stripe/**/*.py"] 27 | python_version = "3.10" 28 | disallow_untyped_defs = "True" 29 | disallow_any_unimported = "True" 30 | no_implicit_optional = "True" 31 | check_untyped_defs = "True" 32 | warn_return_any = "True" 33 | warn_unused_ignores = "True" 34 | show_error_codes = "True" 35 | ignore_missing_imports = "True" 36 | 37 | [tool.pytest.ini_options] 38 | testpaths = ["tests"] 39 | 40 | [tool.coverage.report] 41 | skip_empty = true 42 | -------------------------------------------------------------------------------- /toolkits/stripe/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/stripe/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/web/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/web/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | 45 | [format] 46 | preview = true 47 | skip-magic-trailing-comma = false 48 | -------------------------------------------------------------------------------- /toolkits/web/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/web/arcade_web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/web/arcade_web/__init__.py -------------------------------------------------------------------------------- /toolkits/web/arcade_web/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/web/arcade_web/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/web/arcade_web/tools/models.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | # Models and enums for firecrawl web tools 5 | class Formats(str, Enum): 6 | MARKDOWN = "markdown" 7 | HTML = "html" 8 | RAW_HTML = "rawHtml" 9 | LINKS = "links" 10 | SCREENSHOT = "screenshot" 11 | SCREENSHOT_AT_FULL_PAGE = "screenshot@fullPage" 12 | -------------------------------------------------------------------------------- /toolkits/web/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_web" 3 | version = "1.0.1" 4 | description = "Arcade.dev LLM tools for web scraping related tasks" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=1.0.5,<2.0" 10 | firecrawl-py = "^1.3.1" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_web/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/web/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/web/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/x/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/x/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/x/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/x/arcade_x/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/x/arcade_x/__init__.py -------------------------------------------------------------------------------- /toolkits/x/arcade_x/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/x/arcade_x/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/x/arcade_x/tools/constants.py: -------------------------------------------------------------------------------- 1 | TWEETS_URL = "https://api.x.com/2/tweets" 2 | -------------------------------------------------------------------------------- /toolkits/x/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from arcade.sdk import ToolContext 3 | 4 | 5 | @pytest.fixture 6 | def tool_context(): 7 | """Fixture for the ToolContext with mock authorization.""" 8 | return ToolContext(authorization={"token": "test_token", "user_id": "test_user"}) 9 | 10 | 11 | @pytest.fixture 12 | def mock_httpx_client(mocker): 13 | """Fixture to mock the httpx.AsyncClient.""" 14 | # Mock the AsyncClient context manager 15 | mock_client = mocker.patch("httpx.AsyncClient", autospec=True) 16 | async_mock_client = mock_client.return_value.__aenter__.return_value 17 | return async_mock_client 18 | -------------------------------------------------------------------------------- /toolkits/x/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_x" 3 | version = "0.1.12" 4 | description = "Arcade.dev LLM tools for X (Twitter)" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=0.1,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_x/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | 39 | [tool.pytest.ini_options] 40 | testpaths = ["tests"] 41 | 42 | [tool.coverage.report] 43 | skip_empty = true 44 | -------------------------------------------------------------------------------- /toolkits/x/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/x/tests/__init__.py -------------------------------------------------------------------------------- /toolkits/zoom/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^./ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: "v4.4.0" 5 | hooks: 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: v0.6.7 15 | hooks: 16 | - id: ruff 17 | args: [--fix] 18 | - id: ruff-format 19 | -------------------------------------------------------------------------------- /toolkits/zoom/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 100 3 | fix = true 4 | 5 | [lint] 6 | select = [ 7 | # flake8-2020 8 | "YTT", 9 | # flake8-bandit 10 | "S", 11 | # flake8-bugbear 12 | "B", 13 | # flake8-builtins 14 | "A", 15 | # flake8-comprehensions 16 | "C4", 17 | # flake8-debugger 18 | "T10", 19 | # flake8-simplify 20 | "SIM", 21 | # isort 22 | "I", 23 | # mccabe 24 | "C90", 25 | # pycodestyle 26 | "E", "W", 27 | # pyflakes 28 | "F", 29 | # pygrep-hooks 30 | "PGH", 31 | # pyupgrade 32 | "UP", 33 | # ruff 34 | "RUF", 35 | # tryceratops 36 | "TRY", 37 | ] 38 | 39 | [lint.per-file-ignores] 40 | "*" = ["TRY003", "B904"] 41 | "**/tests/*" = ["S101", "E501"] 42 | "**/evals/*" = ["S101", "E501"] 43 | 44 | [format] 45 | preview = true 46 | skip-magic-trailing-comma = false 47 | -------------------------------------------------------------------------------- /toolkits/zoom/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025, Arcade AI 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 | -------------------------------------------------------------------------------- /toolkits/zoom/arcade_zoom/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/zoom/arcade_zoom/__init__.py -------------------------------------------------------------------------------- /toolkits/zoom/arcade_zoom/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/zoom/arcade_zoom/tools/__init__.py -------------------------------------------------------------------------------- /toolkits/zoom/arcade_zoom/tools/constants.py: -------------------------------------------------------------------------------- 1 | ZOOM_BASE_URL = "https://api.zoom.us/v2" 2 | -------------------------------------------------------------------------------- /toolkits/zoom/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "arcade_zoom" 3 | version = "0.1.10" 4 | description = "Arcade.dev LLM tools for Zoom" 5 | authors = ["Arcade "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | arcade-ai = ">=0.1,<2.0" 10 | httpx = "^0.27.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | pytest = "^8.3.0" 14 | pytest-cov = "^4.0.0" 15 | pytest-asyncio = "^0.24.0" 16 | pytest-mock = "^3.11.1" 17 | mypy = "^1.5.1" 18 | pre-commit = "^3.4.0" 19 | tox = "^4.11.1" 20 | ruff = "^0.7.4" 21 | 22 | [build-system] 23 | requires = ["poetry-core>=1.0.0,<2.0.0"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.mypy] 27 | files = ["arcade_zoom/**/*.py"] 28 | python_version = "3.10" 29 | disallow_untyped_defs = "True" 30 | disallow_any_unimported = "True" 31 | no_implicit_optional = "True" 32 | check_untyped_defs = "True" 33 | warn_return_any = "True" 34 | warn_unused_ignores = "True" 35 | show_error_codes = "True" 36 | ignore_missing_imports = "True" 37 | 38 | [tool.pytest.ini_options] 39 | testpaths = ["tests"] 40 | 41 | [tool.coverage.report] 42 | skip_empty = true 43 | -------------------------------------------------------------------------------- /toolkits/zoom/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArcadeAI/arcade-ai/bc8814e24fc8dfbcb206e9505b1266c4e2f11455/toolkits/zoom/tests/__init__.py -------------------------------------------------------------------------------- /worker.toml: -------------------------------------------------------------------------------- 1 | ### Worker 1 2 | [[worker]] 3 | [worker.config] 4 | id = "worker-1" 5 | enabled = true 6 | timeout = 10 7 | retries = 3 8 | secret = "test-secret" 9 | 10 | [worker.pypi_source] 11 | packages = ["arcade-slack", "arcade-x", "arcade-github"] 12 | 13 | [worker.local_source] 14 | packages = ["./toolkits/spotify"] 15 | --------------------------------------------------------------------------------