gumloop 0.5.2__tar.gz → 0.5.3__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.3}/PKG-INFO +1 -1
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_client.py +10 -2
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_version.py +1 -1
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/__init__.py +4 -0
- gumloop-0.5.3/src/gumloop/resources/decisions.py +72 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/__init__.py +27 -0
- gumloop-0.5.3/src/gumloop/spec/_decisions.py +101 -0
- gumloop-0.5.3/tests/sdk/test_decisions.py +67 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.env.example +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.github/workflows/ci.yml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.github/workflows/release.yml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.gitignore +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.pre-commit-config.yaml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/.python-version +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/LICENSE +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/README.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/pyproject.toml +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/scripts/check +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/scripts/fmt +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/scripts/import-logins.sh +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/scripts/install.sh +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/scripts/sdk_examples.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_auth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_gumcp_transport.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_http.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/brain_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/chromium_cookies.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/discovery.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/extract.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/filter.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/firefox_cookies.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_args_input.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_downloads.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_sync_output.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/auth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/browser.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/plugin.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/update.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/console.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/context.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/credentials.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/main.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/plugin.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/SKILL.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/references/commands.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/py.typed +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/browser_profiles.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/organizations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/teams.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/_compat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/_extensions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/archive.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/bundle.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/bundle_validation.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/hashing.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/installed.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/local_state.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/lock.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/markers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/reconcile.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/reporting.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/results.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/run.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/scheduler.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/target_files.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/targets.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/wire.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/types.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/sync_scenario_helpers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/sync_test_fakes.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_args_input.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_brain_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_browser_logins.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_context.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_credentials.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_home_isolation.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_login.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_main.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_plugin.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_bundle.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_command.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_command_safety.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_contract_fixtures.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_enrollment.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_fixture_corpus.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_hashing.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_installed.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_reconciliation_safety.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_scheduler_macos.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_targets.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/README.md +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/generated-bundle-transfer-limit.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-absolute-path.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-bytes-per-file-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-device.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-duplicate-entry.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-fifo.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-files-per-skill-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-nul-name.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-parent-traversal.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-reserved-marker.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-socket.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-symlink.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-total-uncompressed-limit.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-windows-drive-prefix.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill-stored.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/bundles/newer-than-plan.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/bundles/normal.zip +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/catalog.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/corpus.sha256 +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/hashes/content-v1.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/hashes/manifest-v1.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-identity.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-install-name.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-manifest-hash.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/newer-than-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/normal-bundle.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/below-pro.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/cli-upgrade-required.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/lost-membership.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/normal-empty-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/normal-plan.json +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/integration/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/integration/test_live.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/__init__.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/conftest.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/helpers.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_agents.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_artifacts.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_brain.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_chat.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_client.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_errors.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_evaluations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_gumcp_transport.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_mcp.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_models.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_oauth.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_organizations.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_sessions.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_skills.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_sync.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_teams.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/skill_sync_fixtures.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/tests/test_client.py +0 -0
- {gumloop-0.5.2 → gumloop-0.5.3}/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",
|
|
@@ -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
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import respx
|
|
7
|
+
|
|
8
|
+
from gumloop import AsyncGumloop
|
|
9
|
+
from gumloop import Gumloop
|
|
10
|
+
from gumloop.spec import DecisionsChoiceAnswer
|
|
11
|
+
from gumloop.spec import DecisionsNoulAnswer
|
|
12
|
+
from gumloop.spec import DecisionsRequest
|
|
13
|
+
from tests.sdk.helpers import API_BASE
|
|
14
|
+
from tests.sdk.helpers import request_json
|
|
15
|
+
|
|
16
|
+
RESPONSE = {
|
|
17
|
+
"id": "dec-1",
|
|
18
|
+
"model": "typesafe/jev-1.13",
|
|
19
|
+
"answers": {
|
|
20
|
+
"is_bug": {"type": "noul", "noul": 0.91},
|
|
21
|
+
"segment": {"type": "choice", "choice": "enterprise", "probabilities": {"enterprise": 0.8, "smb": 0.2}},
|
|
22
|
+
},
|
|
23
|
+
"usage": {"input_tokens": 120, "output_tokens": 0, "cost": 0.0001},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
QUESTIONS = {
|
|
27
|
+
"is_bug": {"type": "noul", "instructions": "Is this a software defect?"},
|
|
28
|
+
"segment": {"type": "choice", "instructions": "Which segment?", "criteria": {"enterprise": None, "smb": None}},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@respx.mock
|
|
33
|
+
def test_decisions_create_returns_typed_answers(client: Gumloop) -> None:
|
|
34
|
+
route = respx.post(f"{API_BASE}/decisions").mock(return_value=httpx.Response(200, json=RESPONSE))
|
|
35
|
+
|
|
36
|
+
result = client.decisions.create(model="typesafe/jev-1.13", state={"ticket": "blank screen"}, questions=QUESTIONS)
|
|
37
|
+
|
|
38
|
+
is_bug, segment = result.answers["is_bug"], result.answers["segment"]
|
|
39
|
+
assert isinstance(is_bug, DecisionsNoulAnswer) and is_bug.noul == 0.91
|
|
40
|
+
assert isinstance(segment, DecisionsChoiceAnswer) and segment.choice == "enterprise"
|
|
41
|
+
assert result.usage.input_tokens == 120
|
|
42
|
+
body = request_json(route.calls[0].request)
|
|
43
|
+
assert body["model"] == "typesafe/jev-1.13"
|
|
44
|
+
assert body["state"] == {"ticket": "blank screen"}
|
|
45
|
+
assert body["questions"]["segment"]["criteria"] == {"enterprise": None, "smb": None}
|
|
46
|
+
assert "provider" not in body
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@respx.mock
|
|
50
|
+
def test_decisions_accepts_request_instance(client: Gumloop) -> None:
|
|
51
|
+
route = respx.post(f"{API_BASE}/decisions").mock(return_value=httpx.Response(200, json=RESPONSE))
|
|
52
|
+
request = DecisionsRequest(model="typesafe/jev-1.13", state="x", questions=QUESTIONS)
|
|
53
|
+
|
|
54
|
+
client.decisions.create(request)
|
|
55
|
+
|
|
56
|
+
assert request_json(route.calls[0].request)["state"] == "x"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@respx.mock
|
|
60
|
+
def test_async_decisions_create(async_client: AsyncGumloop) -> None:
|
|
61
|
+
respx.post(f"{API_BASE}/decisions").mock(return_value=httpx.Response(200, json=RESPONSE))
|
|
62
|
+
|
|
63
|
+
result = asyncio.run(
|
|
64
|
+
async_client.decisions.create(model="typesafe/jev-1.13", state={"ticket": "x"}, questions=QUESTIONS)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
assert result.model == "typesafe/jev-1.13"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-absolute-path.zip
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-duplicate-entry.zip
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-parent-traversal.zip
RENAMED
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-reserved-marker.zip
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill-stored.zip
RENAMED
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill.zip
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-manifest-hash.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/cli-upgrade-required.json
RENAMED
|
File without changes
|
|
File without changes
|
{gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/normal-empty-plan.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|