gumloop 0.5.2__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gumloop-0.5.2 → gumloop-0.5.4}/PKG-INFO +1 -1
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/_client.py +10 -2
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/_version.py +1 -1
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/__init__.py +4 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/agents.py +231 -6
- gumloop-0.5.4/src/gumloop/resources/decisions.py +72 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/spec/__init__.py +27 -0
- gumloop-0.5.4/src/gumloop/spec/_decisions.py +101 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/types.py +333 -8
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/integration/test_live.py +11 -7
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_agents.py +290 -2
- gumloop-0.5.4/tests/sdk/test_decisions.py +67 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.env.example +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.github/workflows/ci.yml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.github/workflows/release.yml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.gitignore +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.pre-commit-config.yaml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/.python-version +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/LICENSE +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/README.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/pyproject.toml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/scripts/check +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/scripts/fmt +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/scripts/import-logins.sh +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/scripts/install.sh +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/scripts/sdk_examples.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/_auth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/_gumcp_transport.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/_http.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/brain_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/chromium_cookies.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/discovery.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/extract.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/filter.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/browser_logins/firefox_cookies.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/_args_input.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/_downloads.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/_sync_output.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/auth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/browser.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/plugin.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/commands/update.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/console.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/context.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/credentials.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/main.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/plugin_assets/gumloop/plugin.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/SKILL.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/references/commands.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/py.typed +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/browser_profiles.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/organizations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/resources/teams.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/spec/_compat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/spec/_extensions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/archive.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/bundle.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/bundle_validation.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/hashing.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/installed.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/local_state.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/lock.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/markers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/reconcile.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/reporting.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/results.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/run.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/scheduler.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/target_files.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/targets.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/src/gumloop/sync/wire.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/sync_scenario_helpers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/sync_test_fakes.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_args_input.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_brain_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_browser_logins.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_context.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_credentials.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_home_isolation.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_login.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_main.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_plugin.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_bundle.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_command.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_command_safety.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_contract_fixtures.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_enrollment.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_fixture_corpus.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_hashing.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_installed.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_reconciliation_safety.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_scheduler_macos.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/cli/test_sync_targets.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/README.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/generated-bundle-transfer-limit.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-absolute-path.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-bytes-per-file-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-device.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-duplicate-entry.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-fifo.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-files-per-skill-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-nul-name.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-parent-traversal.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-reserved-marker.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-socket.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-symlink.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-total-uncompressed-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/invalid-windows-drive-prefix.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/valid-single-skill-stored.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/archives/valid-single-skill.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/bundles/newer-than-plan.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/bundles/normal.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/catalog.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/corpus.sha256 +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/hashes/content-v1.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/hashes/manifest-v1.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-identity.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-install-name.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/manifests/invalid-manifest-hash.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/manifests/newer-than-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/manifests/normal-bundle.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/responses/below-pro.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/responses/cli-upgrade-required.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/responses/lost-membership.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/responses/normal-empty-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/fixtures/skill_sync/v1/responses/normal-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/integration/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/helpers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_client.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_gumcp_transport.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_organizations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/sdk/test_teams.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/skill_sync_fixtures.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/tests/test_client.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.4}/uv.lock +0 -0
|
@@ -20,6 +20,7 @@ from gumloop.resources import AsyncArtifacts
|
|
|
20
20
|
from gumloop.resources import AsyncBrain
|
|
21
21
|
from gumloop.resources import AsyncBrowserProfiles
|
|
22
22
|
from gumloop.resources import AsyncChat
|
|
23
|
+
from gumloop.resources import AsyncDecisions
|
|
23
24
|
from gumloop.resources import AsyncEvaluations
|
|
24
25
|
from gumloop.resources import AsyncMCP
|
|
25
26
|
from gumloop.resources import AsyncModels
|
|
@@ -30,6 +31,7 @@ from gumloop.resources import AsyncTeams
|
|
|
30
31
|
from gumloop.resources import Brain
|
|
31
32
|
from gumloop.resources import BrowserProfiles
|
|
32
33
|
from gumloop.resources import Chat
|
|
34
|
+
from gumloop.resources import Decisions
|
|
33
35
|
from gumloop.resources import Evaluations
|
|
34
36
|
from gumloop.resources import Models
|
|
35
37
|
from gumloop.resources import Organizations
|
|
@@ -79,7 +81,9 @@ class Gumloop:
|
|
|
79
81
|
self.team_id = team_id or os.environ.get("GUMLOOP_TEAM_ID")
|
|
80
82
|
# Defaults to production; GUMLOOP_BASE_URL is an optional override.
|
|
81
83
|
self.base_url = (base_url or os.environ.get("GUMLOOP_BASE_URL") or DEFAULT_BASE_URL).rstrip("/")
|
|
82
|
-
self.stream_base_url = (
|
|
84
|
+
self.stream_base_url = (
|
|
85
|
+
stream_base_url or os.environ.get("GUMLOOP_STREAM_BASE_URL") or _derive_stream_base_url(self.base_url)
|
|
86
|
+
).rstrip("/")
|
|
83
87
|
self.timeout = timeout
|
|
84
88
|
self.stream_timeout = stream_timeout
|
|
85
89
|
|
|
@@ -96,6 +100,7 @@ class Gumloop:
|
|
|
96
100
|
self.agents = Agents(self._http)
|
|
97
101
|
self.sessions = Sessions(self._http)
|
|
98
102
|
self.chat = Chat(self._http)
|
|
103
|
+
self.decisions = Decisions(self._http)
|
|
99
104
|
self.models = Models(self._http)
|
|
100
105
|
self.mcp = MCP(self._http)
|
|
101
106
|
self.teams = Teams(self._http)
|
|
@@ -141,7 +146,9 @@ class AsyncGumloop:
|
|
|
141
146
|
self.team_id = team_id or os.environ.get("GUMLOOP_TEAM_ID")
|
|
142
147
|
# Defaults to production; GUMLOOP_BASE_URL is an optional override.
|
|
143
148
|
self.base_url = (base_url or os.environ.get("GUMLOOP_BASE_URL") or DEFAULT_BASE_URL).rstrip("/")
|
|
144
|
-
self.stream_base_url = (
|
|
149
|
+
self.stream_base_url = (
|
|
150
|
+
stream_base_url or os.environ.get("GUMLOOP_STREAM_BASE_URL") or _derive_stream_base_url(self.base_url)
|
|
151
|
+
).rstrip("/")
|
|
145
152
|
self.timeout = timeout
|
|
146
153
|
self.stream_timeout = stream_timeout
|
|
147
154
|
|
|
@@ -158,6 +165,7 @@ class AsyncGumloop:
|
|
|
158
165
|
self.agents = AsyncAgents(self._http)
|
|
159
166
|
self.sessions = AsyncSessions(self._http)
|
|
160
167
|
self.chat = AsyncChat(self._http)
|
|
168
|
+
self.decisions = AsyncDecisions(self._http)
|
|
161
169
|
self.models = AsyncModels(self._http)
|
|
162
170
|
self.mcp = AsyncMCP(self._http)
|
|
163
171
|
self.teams = AsyncTeams(self._http)
|
|
@@ -12,6 +12,8 @@ from gumloop.resources.browser_profiles import AsyncBrowserProfiles
|
|
|
12
12
|
from gumloop.resources.browser_profiles import BrowserProfiles
|
|
13
13
|
from gumloop.resources.chat import AsyncChat
|
|
14
14
|
from gumloop.resources.chat import Chat
|
|
15
|
+
from gumloop.resources.decisions import AsyncDecisions
|
|
16
|
+
from gumloop.resources.decisions import Decisions
|
|
15
17
|
from gumloop.resources.evaluations import AsyncEvaluations
|
|
16
18
|
from gumloop.resources.evaluations import Evaluations
|
|
17
19
|
from gumloop.resources.mcp import MCP
|
|
@@ -35,6 +37,7 @@ __all__ = [
|
|
|
35
37
|
"AsyncBrain",
|
|
36
38
|
"AsyncBrowserProfiles",
|
|
37
39
|
"AsyncChat",
|
|
40
|
+
"AsyncDecisions",
|
|
38
41
|
"AsyncEvaluations",
|
|
39
42
|
"AsyncMCP",
|
|
40
43
|
"AsyncModels",
|
|
@@ -45,6 +48,7 @@ __all__ = [
|
|
|
45
48
|
"Brain",
|
|
46
49
|
"BrowserProfiles",
|
|
47
50
|
"Chat",
|
|
51
|
+
"Decisions",
|
|
48
52
|
"Evaluations",
|
|
49
53
|
"Models",
|
|
50
54
|
"Organizations",
|
|
@@ -7,15 +7,29 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from gumloop._http import AsyncHttpClient
|
|
9
9
|
from gumloop._http import HttpClient
|
|
10
|
+
from gumloop.types import AgentAbilitiesResponse
|
|
11
|
+
from gumloop.types import AgentAbilitiesUpdateRequest
|
|
12
|
+
from gumloop.types import AgentAppRulesResponse
|
|
10
13
|
from gumloop.types import AgentCreateRequest
|
|
14
|
+
from gumloop.types import AgentDeleteResponse
|
|
11
15
|
from gumloop.types import AgentEvaluationMetricsResponse
|
|
12
16
|
from gumloop.types import AgentEvaluationOptionsResponse
|
|
17
|
+
from gumloop.types import AgentKnowledgeSourceDetachResponse
|
|
18
|
+
from gumloop.types import AgentKnowledgeSourceResponse
|
|
19
|
+
from gumloop.types import AgentKnowledgeSourcesResponse
|
|
13
20
|
from gumloop.types import AgentListResponse
|
|
14
21
|
from gumloop.types import AgentMcpServerDetachResponse
|
|
15
22
|
from gumloop.types import AgentMcpServerResponse
|
|
16
23
|
from gumloop.types import AgentMcpServersResponse
|
|
17
24
|
from gumloop.types import AgentResponse
|
|
18
25
|
from gumloop.types import AgentSkillsResponse
|
|
26
|
+
from gumloop.types import AgentSubagentsResponse
|
|
27
|
+
from gumloop.types import AgentTriggerCreateRequest
|
|
28
|
+
from gumloop.types import AgentTriggerDeleteResponse
|
|
29
|
+
from gumloop.types import AgentTriggerResponse
|
|
30
|
+
from gumloop.types import AgentTriggersResponse
|
|
31
|
+
from gumloop.types import AgentTriggerUpdateRequest
|
|
32
|
+
from gumloop.types import AgentTriggerWebhookUrlResponse
|
|
19
33
|
from gumloop.types import AgentUpdateRequest
|
|
20
34
|
from gumloop.types import AgentVersionResponse
|
|
21
35
|
from gumloop.types import AgentVersionsResponse
|
|
@@ -25,6 +39,7 @@ from gumloop.types import EvaluationResultListResponse
|
|
|
25
39
|
from gumloop.types import EvaluationResultResponse
|
|
26
40
|
from gumloop.types import EvaluationRunRequest
|
|
27
41
|
from gumloop.types import EvaluationRunResponse
|
|
42
|
+
from gumloop.types import KnowledgeSourceScope
|
|
28
43
|
from gumloop.types import ModelListResponse
|
|
29
44
|
from gumloop.types import ModelRouteRequest
|
|
30
45
|
from gumloop.types import ModelRouteResponse
|
|
@@ -35,6 +50,14 @@ def _skill_id_list(skill_ids: str | Sequence[str]) -> list[str]:
|
|
|
35
50
|
return [skill_ids] if isinstance(skill_ids, str) else list(skill_ids)
|
|
36
51
|
|
|
37
52
|
|
|
53
|
+
def _knowledge_scope_body(config: KnowledgeSourceScope | Mapping[str, Any] | None) -> dict[str, Any]:
|
|
54
|
+
# The key is always sent: null means the whole source.
|
|
55
|
+
if config is None:
|
|
56
|
+
return {"config": None}
|
|
57
|
+
scope = config if isinstance(config, KnowledgeSourceScope) else KnowledgeSourceScope.model_validate(config)
|
|
58
|
+
return {"config": scope.model_dump(exclude_unset=True)}
|
|
59
|
+
|
|
60
|
+
|
|
38
61
|
class Agents:
|
|
39
62
|
def __init__(self, client: HttpClient) -> None:
|
|
40
63
|
self._client = client
|
|
@@ -99,9 +122,11 @@ class Agents:
|
|
|
99
122
|
request: AgentUpdateRequest | Mapping[str, Any] | None = None,
|
|
100
123
|
**kwargs: Any,
|
|
101
124
|
) -> AgentResponse:
|
|
102
|
-
"""
|
|
103
|
-
|
|
104
|
-
|
|
125
|
+
"""Settings live under ``metadata`` and merge section by section. Pass ``version``
|
|
126
|
+
from a previous read to refuse the update if the agent changed since
|
|
127
|
+
(``agent_version_conflict``). ``tools`` replaces the whole list when provided;
|
|
128
|
+
prefer update_abilities, attach_mcp_server/detach_mcp_server and
|
|
129
|
+
attach_skills/detach_skills."""
|
|
105
130
|
return AgentResponse.model_validate(
|
|
106
131
|
self._client.patch(f"agents/{agent_id}", json=AgentUpdateRequest.build(request, **kwargs))
|
|
107
132
|
)
|
|
@@ -139,6 +164,111 @@ class Agents:
|
|
|
139
164
|
def list_mcp_servers(self, agent_id: str) -> AgentMcpServersResponse:
|
|
140
165
|
return AgentMcpServersResponse.model_validate(self._client.get(f"agents/{agent_id}/mcp-servers"))
|
|
141
166
|
|
|
167
|
+
def delete(self, agent_id: str) -> AgentDeleteResponse:
|
|
168
|
+
"""Delete a custom agent. Platform agents (``gumball``, ``analytics``) are refused."""
|
|
169
|
+
return AgentDeleteResponse.model_validate(self._client.delete(f"agents/{agent_id}"))
|
|
170
|
+
|
|
171
|
+
def update_abilities(
|
|
172
|
+
self,
|
|
173
|
+
agent_id: str,
|
|
174
|
+
request: AgentAbilitiesUpdateRequest | Mapping[str, Any] | None = None,
|
|
175
|
+
**abilities: Any,
|
|
176
|
+
) -> AgentAbilitiesResponse:
|
|
177
|
+
"""Turn native abilities on or off and set their options, e.g.
|
|
178
|
+
``update_abilities(agent_id, web_search={"enabled": True, "provider": "exa"},
|
|
179
|
+
ask_question={"enabled": False})``. Only the abilities you send change."""
|
|
180
|
+
return AgentAbilitiesResponse.model_validate(
|
|
181
|
+
self._client.patch(
|
|
182
|
+
f"agents/{agent_id}/abilities",
|
|
183
|
+
json=AgentAbilitiesUpdateRequest.build(request, **abilities),
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def list_knowledge_sources(self, agent_id: str) -> AgentKnowledgeSourcesResponse:
|
|
188
|
+
return AgentKnowledgeSourcesResponse.model_validate(self._client.get(f"agents/{agent_id}/knowledge-sources"))
|
|
189
|
+
|
|
190
|
+
def attach_knowledge_source(
|
|
191
|
+
self,
|
|
192
|
+
agent_id: str,
|
|
193
|
+
connector_id: str,
|
|
194
|
+
*,
|
|
195
|
+
config: KnowledgeSourceScope | Mapping[str, Any] | None = None,
|
|
196
|
+
) -> AgentKnowledgeSourceResponse:
|
|
197
|
+
"""Attach a Brain source, or change its scope if already attached (idempotent upsert).
|
|
198
|
+
``config=None`` attaches the whole source."""
|
|
199
|
+
return AgentKnowledgeSourceResponse.model_validate(
|
|
200
|
+
self._client.put(
|
|
201
|
+
f"agents/{agent_id}/knowledge-sources/{connector_id}",
|
|
202
|
+
json=_knowledge_scope_body(config),
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def detach_knowledge_source(self, agent_id: str, connector_id: str) -> AgentKnowledgeSourceDetachResponse:
|
|
207
|
+
"""Detach a Brain source. Idempotent: safe to retry."""
|
|
208
|
+
return AgentKnowledgeSourceDetachResponse.model_validate(
|
|
209
|
+
self._client.delete(f"agents/{agent_id}/knowledge-sources/{connector_id}")
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def attach_subagent(self, agent_id: str, subagent_id: str) -> AgentSubagentsResponse:
|
|
213
|
+
"""Allow the agent to delegate to ``subagent_id``. Idempotent; the subagent must live in the same workspace."""
|
|
214
|
+
return AgentSubagentsResponse.model_validate(self._client.put(f"agents/{agent_id}/subagents/{subagent_id}"))
|
|
215
|
+
|
|
216
|
+
def detach_subagent(self, agent_id: str, subagent_id: str) -> AgentSubagentsResponse:
|
|
217
|
+
return AgentSubagentsResponse.model_validate(self._client.delete(f"agents/{agent_id}/subagents/{subagent_id}"))
|
|
218
|
+
|
|
219
|
+
def list_triggers(
|
|
220
|
+
self,
|
|
221
|
+
agent_id: str,
|
|
222
|
+
*,
|
|
223
|
+
page_size: int | None = None,
|
|
224
|
+
cursor: str | None = None,
|
|
225
|
+
) -> AgentTriggersResponse:
|
|
226
|
+
return AgentTriggersResponse.model_validate(
|
|
227
|
+
self._client.get(f"agents/{agent_id}/triggers", params={"page_size": page_size, "cursor": cursor})
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def create_trigger(
|
|
231
|
+
self,
|
|
232
|
+
agent_id: str,
|
|
233
|
+
request: AgentTriggerCreateRequest | Mapping[str, Any] | None = None,
|
|
234
|
+
**kwargs: Any,
|
|
235
|
+
) -> AgentTriggerResponse:
|
|
236
|
+
"""Create a ``schedule`` (``cron_expression`` or ``run_at``) or ``webhook`` trigger.
|
|
237
|
+
A webhook's URL is returned here and from ``get_trigger_webhook_url``, never in lists."""
|
|
238
|
+
return AgentTriggerResponse.model_validate(
|
|
239
|
+
self._client.post(f"agents/{agent_id}/triggers", json=AgentTriggerCreateRequest.build(request, **kwargs))
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
def update_trigger(
|
|
243
|
+
self,
|
|
244
|
+
agent_id: str,
|
|
245
|
+
trigger_id: str,
|
|
246
|
+
request: AgentTriggerUpdateRequest | Mapping[str, Any] | None = None,
|
|
247
|
+
**kwargs: Any,
|
|
248
|
+
) -> AgentTriggerResponse:
|
|
249
|
+
"""Edit, enable or disable a schedule or webhook trigger. Only the fields you send change."""
|
|
250
|
+
return AgentTriggerResponse.model_validate(
|
|
251
|
+
self._client.patch(
|
|
252
|
+
f"agents/{agent_id}/triggers/{trigger_id}",
|
|
253
|
+
json=AgentTriggerUpdateRequest.build(request, **kwargs),
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def delete_trigger(self, agent_id: str, trigger_id: str) -> AgentTriggerDeleteResponse:
|
|
258
|
+
return AgentTriggerDeleteResponse.model_validate(
|
|
259
|
+
self._client.delete(f"agents/{agent_id}/triggers/{trigger_id}")
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def get_trigger_webhook_url(self, agent_id: str, trigger_id: str) -> AgentTriggerWebhookUrlResponse:
|
|
263
|
+
"""The webhook URL embeds its secret; fetch it on demand rather than storing it."""
|
|
264
|
+
return AgentTriggerWebhookUrlResponse.model_validate(
|
|
265
|
+
self._client.get(f"agents/{agent_id}/triggers/{trigger_id}/webhook-url")
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def list_app_rules(self, agent_id: str) -> AgentAppRulesResponse:
|
|
269
|
+
"""Rules the agent has authored for its connectors. Read only."""
|
|
270
|
+
return AgentAppRulesResponse.model_validate(self._client.get(f"agents/{agent_id}/app-rules"))
|
|
271
|
+
|
|
142
272
|
def get_evaluation_options(self) -> AgentEvaluationOptionsResponse:
|
|
143
273
|
return AgentEvaluationOptionsResponse.model_validate(self._client.get("agents/evaluation-options"))
|
|
144
274
|
|
|
@@ -295,9 +425,11 @@ class AsyncAgents:
|
|
|
295
425
|
request: AgentUpdateRequest | Mapping[str, Any] | None = None,
|
|
296
426
|
**kwargs: Any,
|
|
297
427
|
) -> AgentResponse:
|
|
298
|
-
"""
|
|
299
|
-
|
|
300
|
-
|
|
428
|
+
"""Settings live under ``metadata`` and merge section by section. Pass ``version``
|
|
429
|
+
from a previous read to refuse the update if the agent changed since
|
|
430
|
+
(``agent_version_conflict``). ``tools`` replaces the whole list when provided;
|
|
431
|
+
prefer update_abilities, attach_mcp_server/detach_mcp_server and
|
|
432
|
+
attach_skills/detach_skills."""
|
|
301
433
|
data = await self._client.patch(f"agents/{agent_id}", json=AgentUpdateRequest.build(request, **kwargs))
|
|
302
434
|
return AgentResponse.model_validate(data)
|
|
303
435
|
|
|
@@ -332,6 +464,99 @@ class AsyncAgents:
|
|
|
332
464
|
data = await self._client.get(f"agents/{agent_id}/mcp-servers")
|
|
333
465
|
return AgentMcpServersResponse.model_validate(data)
|
|
334
466
|
|
|
467
|
+
async def delete(self, agent_id: str) -> AgentDeleteResponse:
|
|
468
|
+
"""Delete a custom agent. Platform agents (``gumball``, ``analytics``) are refused."""
|
|
469
|
+
return AgentDeleteResponse.model_validate(await self._client.delete(f"agents/{agent_id}"))
|
|
470
|
+
|
|
471
|
+
async def update_abilities(
|
|
472
|
+
self,
|
|
473
|
+
agent_id: str,
|
|
474
|
+
request: AgentAbilitiesUpdateRequest | Mapping[str, Any] | None = None,
|
|
475
|
+
**abilities: Any,
|
|
476
|
+
) -> AgentAbilitiesResponse:
|
|
477
|
+
"""Turn native abilities on or off and set their options. Only the abilities you send change."""
|
|
478
|
+
data = await self._client.patch(
|
|
479
|
+
f"agents/{agent_id}/abilities", json=AgentAbilitiesUpdateRequest.build(request, **abilities)
|
|
480
|
+
)
|
|
481
|
+
return AgentAbilitiesResponse.model_validate(data)
|
|
482
|
+
|
|
483
|
+
async def list_knowledge_sources(self, agent_id: str) -> AgentKnowledgeSourcesResponse:
|
|
484
|
+
data = await self._client.get(f"agents/{agent_id}/knowledge-sources")
|
|
485
|
+
return AgentKnowledgeSourcesResponse.model_validate(data)
|
|
486
|
+
|
|
487
|
+
async def attach_knowledge_source(
|
|
488
|
+
self,
|
|
489
|
+
agent_id: str,
|
|
490
|
+
connector_id: str,
|
|
491
|
+
*,
|
|
492
|
+
config: KnowledgeSourceScope | Mapping[str, Any] | None = None,
|
|
493
|
+
) -> AgentKnowledgeSourceResponse:
|
|
494
|
+
"""Attach a Brain source, or change its scope if already attached (idempotent upsert).
|
|
495
|
+
``config=None`` attaches the whole source."""
|
|
496
|
+
data = await self._client.put(
|
|
497
|
+
f"agents/{agent_id}/knowledge-sources/{connector_id}", json=_knowledge_scope_body(config)
|
|
498
|
+
)
|
|
499
|
+
return AgentKnowledgeSourceResponse.model_validate(data)
|
|
500
|
+
|
|
501
|
+
async def detach_knowledge_source(self, agent_id: str, connector_id: str) -> AgentKnowledgeSourceDetachResponse:
|
|
502
|
+
data = await self._client.delete(f"agents/{agent_id}/knowledge-sources/{connector_id}")
|
|
503
|
+
return AgentKnowledgeSourceDetachResponse.model_validate(data)
|
|
504
|
+
|
|
505
|
+
async def attach_subagent(self, agent_id: str, subagent_id: str) -> AgentSubagentsResponse:
|
|
506
|
+
data = await self._client.put(f"agents/{agent_id}/subagents/{subagent_id}")
|
|
507
|
+
return AgentSubagentsResponse.model_validate(data)
|
|
508
|
+
|
|
509
|
+
async def detach_subagent(self, agent_id: str, subagent_id: str) -> AgentSubagentsResponse:
|
|
510
|
+
data = await self._client.delete(f"agents/{agent_id}/subagents/{subagent_id}")
|
|
511
|
+
return AgentSubagentsResponse.model_validate(data)
|
|
512
|
+
|
|
513
|
+
async def list_triggers(
|
|
514
|
+
self,
|
|
515
|
+
agent_id: str,
|
|
516
|
+
*,
|
|
517
|
+
page_size: int | None = None,
|
|
518
|
+
cursor: str | None = None,
|
|
519
|
+
) -> AgentTriggersResponse:
|
|
520
|
+
data = await self._client.get(f"agents/{agent_id}/triggers", params={"page_size": page_size, "cursor": cursor})
|
|
521
|
+
return AgentTriggersResponse.model_validate(data)
|
|
522
|
+
|
|
523
|
+
async def create_trigger(
|
|
524
|
+
self,
|
|
525
|
+
agent_id: str,
|
|
526
|
+
request: AgentTriggerCreateRequest | Mapping[str, Any] | None = None,
|
|
527
|
+
**kwargs: Any,
|
|
528
|
+
) -> AgentTriggerResponse:
|
|
529
|
+
"""Create a ``schedule`` (``cron_expression`` or ``run_at``) or ``webhook`` trigger.
|
|
530
|
+
A webhook's URL is returned here and from ``get_trigger_webhook_url``, never in lists."""
|
|
531
|
+
data = await self._client.post(
|
|
532
|
+
f"agents/{agent_id}/triggers", json=AgentTriggerCreateRequest.build(request, **kwargs)
|
|
533
|
+
)
|
|
534
|
+
return AgentTriggerResponse.model_validate(data)
|
|
535
|
+
|
|
536
|
+
async def update_trigger(
|
|
537
|
+
self,
|
|
538
|
+
agent_id: str,
|
|
539
|
+
trigger_id: str,
|
|
540
|
+
request: AgentTriggerUpdateRequest | Mapping[str, Any] | None = None,
|
|
541
|
+
**kwargs: Any,
|
|
542
|
+
) -> AgentTriggerResponse:
|
|
543
|
+
data = await self._client.patch(
|
|
544
|
+
f"agents/{agent_id}/triggers/{trigger_id}", json=AgentTriggerUpdateRequest.build(request, **kwargs)
|
|
545
|
+
)
|
|
546
|
+
return AgentTriggerResponse.model_validate(data)
|
|
547
|
+
|
|
548
|
+
async def delete_trigger(self, agent_id: str, trigger_id: str) -> AgentTriggerDeleteResponse:
|
|
549
|
+
data = await self._client.delete(f"agents/{agent_id}/triggers/{trigger_id}")
|
|
550
|
+
return AgentTriggerDeleteResponse.model_validate(data)
|
|
551
|
+
|
|
552
|
+
async def get_trigger_webhook_url(self, agent_id: str, trigger_id: str) -> AgentTriggerWebhookUrlResponse:
|
|
553
|
+
data = await self._client.get(f"agents/{agent_id}/triggers/{trigger_id}/webhook-url")
|
|
554
|
+
return AgentTriggerWebhookUrlResponse.model_validate(data)
|
|
555
|
+
|
|
556
|
+
async def list_app_rules(self, agent_id: str) -> AgentAppRulesResponse:
|
|
557
|
+
data = await self._client.get(f"agents/{agent_id}/app-rules")
|
|
558
|
+
return AgentAppRulesResponse.model_validate(data)
|
|
559
|
+
|
|
335
560
|
async def get_evaluation_options(self) -> AgentEvaluationOptionsResponse:
|
|
336
561
|
data = await self._client.get("agents/evaluation-options")
|
|
337
562
|
return AgentEvaluationOptionsResponse.model_validate(data)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Decisions resource — OpenRouter request shape."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import ValidationError
|
|
9
|
+
|
|
10
|
+
from gumloop._http import AsyncHttpClient
|
|
11
|
+
from gumloop._http import HttpClient
|
|
12
|
+
from gumloop.errors import GumloopError
|
|
13
|
+
from gumloop.spec import DecisionsQuestion
|
|
14
|
+
from gumloop.spec import DecisionsRequest
|
|
15
|
+
from gumloop.spec import DecisionsResponse
|
|
16
|
+
from gumloop.spec._compat import to_wire_dict
|
|
17
|
+
from gumloop.spec._decisions import Json
|
|
18
|
+
|
|
19
|
+
_PATH = "decisions"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _build_request(
|
|
23
|
+
request: DecisionsRequest | Mapping[str, Any] | None,
|
|
24
|
+
kwargs: dict[str, Any],
|
|
25
|
+
) -> DecisionsRequest:
|
|
26
|
+
if isinstance(request, DecisionsRequest):
|
|
27
|
+
base = to_wire_dict(request)
|
|
28
|
+
elif request is None:
|
|
29
|
+
base = {}
|
|
30
|
+
else:
|
|
31
|
+
base = dict(request)
|
|
32
|
+
base.update({k: v for k, v in kwargs.items() if v is not None})
|
|
33
|
+
try:
|
|
34
|
+
return DecisionsRequest.model_validate(base)
|
|
35
|
+
except ValidationError as exc:
|
|
36
|
+
raise GumloopError(f"invalid decisions request: {exc}") from exc
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Decisions:
|
|
40
|
+
def __init__(self, client: HttpClient) -> None:
|
|
41
|
+
self._client = client
|
|
42
|
+
|
|
43
|
+
def create(
|
|
44
|
+
self,
|
|
45
|
+
request: DecisionsRequest | Mapping[str, Any] | None = None,
|
|
46
|
+
*,
|
|
47
|
+
model: str | None = None,
|
|
48
|
+
state: Json | None = None,
|
|
49
|
+
questions: Mapping[str, DecisionsQuestion | Mapping[str, Any]] | None = None,
|
|
50
|
+
**kwargs: Any,
|
|
51
|
+
) -> DecisionsResponse:
|
|
52
|
+
body = _build_request(request, {"model": model, "state": state, "questions": questions, **kwargs})
|
|
53
|
+
data = self._client.post(_PATH, json=to_wire_dict(body))
|
|
54
|
+
return DecisionsResponse.model_validate(data)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AsyncDecisions:
|
|
58
|
+
def __init__(self, client: AsyncHttpClient) -> None:
|
|
59
|
+
self._client = client
|
|
60
|
+
|
|
61
|
+
async def create(
|
|
62
|
+
self,
|
|
63
|
+
request: DecisionsRequest | Mapping[str, Any] | None = None,
|
|
64
|
+
*,
|
|
65
|
+
model: str | None = None,
|
|
66
|
+
state: Json | None = None,
|
|
67
|
+
questions: Mapping[str, DecisionsQuestion | Mapping[str, Any]] | None = None,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> DecisionsResponse:
|
|
70
|
+
body = _build_request(request, {"model": model, "state": state, "questions": questions, **kwargs})
|
|
71
|
+
data = await self._client.post(_PATH, json=to_wire_dict(body))
|
|
72
|
+
return DecisionsResponse.model_validate(data)
|
|
@@ -17,6 +17,20 @@ from openrouter.components import ProviderPreferences
|
|
|
17
17
|
from openrouter.components import ResponseHealingPlugin
|
|
18
18
|
from openrouter.components import WebSearchPlugin
|
|
19
19
|
|
|
20
|
+
from gumloop.spec._decisions import DecisionsAnswer
|
|
21
|
+
from gumloop.spec._decisions import DecisionsChoiceAnswer
|
|
22
|
+
from gumloop.spec._decisions import DecisionsChoiceQuestion
|
|
23
|
+
from gumloop.spec._decisions import DecisionsNoulAnswer
|
|
24
|
+
from gumloop.spec._decisions import DecisionsNoulQuestion
|
|
25
|
+
from gumloop.spec._decisions import DecisionsNoulQuestionCriteria
|
|
26
|
+
from gumloop.spec._decisions import DecisionsQuestion
|
|
27
|
+
from gumloop.spec._decisions import DecisionsRequest
|
|
28
|
+
from gumloop.spec._decisions import DecisionsResponse
|
|
29
|
+
from gumloop.spec._decisions import DecisionsResponseUsage
|
|
30
|
+
from gumloop.spec._decisions import DecisionsScoreAnswer
|
|
31
|
+
from gumloop.spec._decisions import DecisionsScoreQuestion
|
|
32
|
+
from gumloop.spec._decisions import TraceConfig
|
|
33
|
+
|
|
20
34
|
# ChatStreamDelta/ChatStreamChoice/ChatStreamChunk/ChatUsage subclassed to add
|
|
21
35
|
# fields Speakeasy 0.9.1 drops + Gumloop-only `gumloop_extensions` bucket.
|
|
22
36
|
from gumloop.spec._extensions import ChatStreamChunk
|
|
@@ -36,9 +50,22 @@ __all__ = [
|
|
|
36
50
|
"ChatStreamDelta",
|
|
37
51
|
"ChatToolChoice",
|
|
38
52
|
"ChatUsage",
|
|
53
|
+
"DecisionsAnswer",
|
|
54
|
+
"DecisionsChoiceAnswer",
|
|
55
|
+
"DecisionsChoiceQuestion",
|
|
56
|
+
"DecisionsNoulAnswer",
|
|
57
|
+
"DecisionsNoulQuestion",
|
|
58
|
+
"DecisionsNoulQuestionCriteria",
|
|
59
|
+
"DecisionsQuestion",
|
|
60
|
+
"DecisionsRequest",
|
|
61
|
+
"DecisionsResponse",
|
|
62
|
+
"DecisionsResponseUsage",
|
|
63
|
+
"DecisionsScoreAnswer",
|
|
64
|
+
"DecisionsScoreQuestion",
|
|
39
65
|
"FormatJSONObjectConfig",
|
|
40
66
|
"ImageConfig",
|
|
41
67
|
"ProviderPreferences",
|
|
42
68
|
"ResponseHealingPlugin",
|
|
69
|
+
"TraceConfig",
|
|
43
70
|
"WebSearchPlugin",
|
|
44
71
|
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""OpenRouter Decisions models, named as in ``openrouter.components`` 1.3.14;
|
|
2
|
+
the pinned ``openrouter`` predates them."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
from typing import Any
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from openrouter.components import ProviderPreferences
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
from pydantic import Field
|
|
13
|
+
|
|
14
|
+
Json = str | dict[str, Any] | list[Any]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DecisionsNoulQuestionCriteria(BaseModel):
|
|
18
|
+
true: Json | None = None
|
|
19
|
+
false: Json | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DecisionsNoulQuestion(BaseModel):
|
|
23
|
+
type: Literal["noul"]
|
|
24
|
+
instructions: Json
|
|
25
|
+
criteria: DecisionsNoulQuestionCriteria | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DecisionsChoiceQuestion(BaseModel):
|
|
29
|
+
type: Literal["choice"]
|
|
30
|
+
instructions: Json
|
|
31
|
+
criteria: dict[str, Json | None]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DecisionsScoreQuestion(BaseModel):
|
|
35
|
+
type: Literal["score"]
|
|
36
|
+
instructions: Json
|
|
37
|
+
criteria: list[Json]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
DecisionsQuestion = Annotated[
|
|
41
|
+
DecisionsNoulQuestion | DecisionsChoiceQuestion | DecisionsScoreQuestion,
|
|
42
|
+
Field(discriminator="type"),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TraceConfig(BaseModel):
|
|
47
|
+
trace_id: str | None = None
|
|
48
|
+
trace_name: str | None = None
|
|
49
|
+
span_name: str | None = None
|
|
50
|
+
generation_name: str | None = None
|
|
51
|
+
parent_span_id: str | None = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DecisionsRequest(BaseModel):
|
|
55
|
+
model: str
|
|
56
|
+
state: Json
|
|
57
|
+
questions: dict[str, DecisionsQuestion]
|
|
58
|
+
provider: ProviderPreferences | None = None
|
|
59
|
+
session_id: str | None = Field(default=None, max_length=256)
|
|
60
|
+
trace: TraceConfig | None = None
|
|
61
|
+
user: str | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class DecisionsNoulAnswer(BaseModel):
|
|
65
|
+
type: Literal["noul"]
|
|
66
|
+
noul: float
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DecisionsChoiceAnswer(BaseModel):
|
|
70
|
+
type: Literal["choice"]
|
|
71
|
+
choice: str
|
|
72
|
+
confidence: float | None = None
|
|
73
|
+
probabilities: dict[str, float] | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class DecisionsScoreAnswer(BaseModel):
|
|
77
|
+
type: Literal["score"]
|
|
78
|
+
score: float
|
|
79
|
+
confidence: float | None = None
|
|
80
|
+
legend: dict[str, Json] | None = None
|
|
81
|
+
probabilities: dict[str, float] | None = None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
DecisionsAnswer = Annotated[
|
|
85
|
+
DecisionsNoulAnswer | DecisionsChoiceAnswer | DecisionsScoreAnswer,
|
|
86
|
+
Field(discriminator="type"),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DecisionsResponseUsage(BaseModel):
|
|
91
|
+
input_tokens: int
|
|
92
|
+
output_tokens: int
|
|
93
|
+
cost: float | None = None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class DecisionsResponse(BaseModel):
|
|
97
|
+
answers: dict[str, DecisionsAnswer]
|
|
98
|
+
model: str
|
|
99
|
+
usage: DecisionsResponseUsage
|
|
100
|
+
id: str | None = None
|
|
101
|
+
provider: str | None = None
|