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.
Files changed (189) hide show
  1. {gumloop-0.5.2 → gumloop-0.5.3}/PKG-INFO +1 -1
  2. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_client.py +10 -2
  3. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_version.py +1 -1
  4. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/__init__.py +4 -0
  5. gumloop-0.5.3/src/gumloop/resources/decisions.py +72 -0
  6. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/__init__.py +27 -0
  7. gumloop-0.5.3/src/gumloop/spec/_decisions.py +101 -0
  8. gumloop-0.5.3/tests/sdk/test_decisions.py +67 -0
  9. {gumloop-0.5.2 → gumloop-0.5.3}/.env.example +0 -0
  10. {gumloop-0.5.2 → gumloop-0.5.3}/.github/workflows/ci.yml +0 -0
  11. {gumloop-0.5.2 → gumloop-0.5.3}/.github/workflows/release.yml +0 -0
  12. {gumloop-0.5.2 → gumloop-0.5.3}/.gitignore +0 -0
  13. {gumloop-0.5.2 → gumloop-0.5.3}/.pre-commit-config.yaml +0 -0
  14. {gumloop-0.5.2 → gumloop-0.5.3}/.python-version +0 -0
  15. {gumloop-0.5.2 → gumloop-0.5.3}/LICENSE +0 -0
  16. {gumloop-0.5.2 → gumloop-0.5.3}/README.md +0 -0
  17. {gumloop-0.5.2 → gumloop-0.5.3}/pyproject.toml +0 -0
  18. {gumloop-0.5.2 → gumloop-0.5.3}/scripts/check +0 -0
  19. {gumloop-0.5.2 → gumloop-0.5.3}/scripts/fmt +0 -0
  20. {gumloop-0.5.2 → gumloop-0.5.3}/scripts/import-logins.sh +0 -0
  21. {gumloop-0.5.2 → gumloop-0.5.3}/scripts/install.sh +0 -0
  22. {gumloop-0.5.2 → gumloop-0.5.3}/scripts/sdk_examples.py +0 -0
  23. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/__init__.py +0 -0
  24. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_auth.py +0 -0
  25. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_gumcp_transport.py +0 -0
  26. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/_http.py +0 -0
  27. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/brain_sync.py +0 -0
  28. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/__init__.py +0 -0
  29. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/chromium_cookies.py +0 -0
  30. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/discovery.py +0 -0
  31. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/extract.py +0 -0
  32. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/filter.py +0 -0
  33. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/browser_logins/firefox_cookies.py +0 -0
  34. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/__init__.py +0 -0
  35. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/__init__.py +0 -0
  36. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_args_input.py +0 -0
  37. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_downloads.py +0 -0
  38. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/_sync_output.py +0 -0
  39. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/agents.py +0 -0
  40. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/artifacts.py +0 -0
  41. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/auth.py +0 -0
  42. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/brain.py +0 -0
  43. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/browser.py +0 -0
  44. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/chat.py +0 -0
  45. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/evaluations.py +0 -0
  46. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/mcp.py +0 -0
  47. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/models.py +0 -0
  48. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/plugin.py +0 -0
  49. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/sessions.py +0 -0
  50. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/skills.py +0 -0
  51. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/sync.py +0 -0
  52. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/commands/update.py +0 -0
  53. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/console.py +0 -0
  54. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/context.py +0 -0
  55. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/credentials.py +0 -0
  56. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/errors.py +0 -0
  57. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/main.py +0 -0
  58. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/oauth.py +0 -0
  59. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/plugin.json +0 -0
  60. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/SKILL.md +0 -0
  61. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/cli/plugin_assets/gumloop/skills/gumloop-cli/references/commands.md +0 -0
  62. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/errors.py +0 -0
  63. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/oauth.py +0 -0
  64. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/py.typed +0 -0
  65. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/agents.py +0 -0
  66. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/artifacts.py +0 -0
  67. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/brain.py +0 -0
  68. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/browser_profiles.py +0 -0
  69. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/chat.py +0 -0
  70. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/evaluations.py +0 -0
  71. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/mcp.py +0 -0
  72. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/organizations.py +0 -0
  73. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/sessions.py +0 -0
  74. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/skills.py +0 -0
  75. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/sync.py +0 -0
  76. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/resources/teams.py +0 -0
  77. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/_compat.py +0 -0
  78. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/spec/_extensions.py +0 -0
  79. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/__init__.py +0 -0
  80. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/archive.py +0 -0
  81. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/bundle.py +0 -0
  82. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/bundle_validation.py +0 -0
  83. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/errors.py +0 -0
  84. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/hashing.py +0 -0
  85. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/installed.py +0 -0
  86. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/local_state.py +0 -0
  87. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/lock.py +0 -0
  88. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/markers.py +0 -0
  89. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/reconcile.py +0 -0
  90. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/reporting.py +0 -0
  91. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/results.py +0 -0
  92. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/run.py +0 -0
  93. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/scheduler.py +0 -0
  94. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/target_files.py +0 -0
  95. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/targets.py +0 -0
  96. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/sync/wire.py +0 -0
  97. {gumloop-0.5.2 → gumloop-0.5.3}/src/gumloop/types.py +0 -0
  98. {gumloop-0.5.2 → gumloop-0.5.3}/tests/__init__.py +0 -0
  99. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/__init__.py +0 -0
  100. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/conftest.py +0 -0
  101. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/sync_scenario_helpers.py +0 -0
  102. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/sync_test_fakes.py +0 -0
  103. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_agents.py +0 -0
  104. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_args_input.py +0 -0
  105. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_artifacts.py +0 -0
  106. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_brain.py +0 -0
  107. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_brain_sync.py +0 -0
  108. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_browser_logins.py +0 -0
  109. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_chat.py +0 -0
  110. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_context.py +0 -0
  111. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_credentials.py +0 -0
  112. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_evaluations.py +0 -0
  113. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_home_isolation.py +0 -0
  114. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_login.py +0 -0
  115. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_main.py +0 -0
  116. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_mcp.py +0 -0
  117. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_models.py +0 -0
  118. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_plugin.py +0 -0
  119. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sessions.py +0 -0
  120. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_skills.py +0 -0
  121. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_bundle.py +0 -0
  122. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_command.py +0 -0
  123. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_command_safety.py +0 -0
  124. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_contract_fixtures.py +0 -0
  125. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_enrollment.py +0 -0
  126. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_fixture_corpus.py +0 -0
  127. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_hashing.py +0 -0
  128. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_installed.py +0 -0
  129. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_reconciliation_safety.py +0 -0
  130. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_scheduler_macos.py +0 -0
  131. {gumloop-0.5.2 → gumloop-0.5.3}/tests/cli/test_sync_targets.py +0 -0
  132. {gumloop-0.5.2 → gumloop-0.5.3}/tests/conftest.py +0 -0
  133. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/README.md +0 -0
  134. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/generated-bundle-transfer-limit.json +0 -0
  135. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-absolute-path.zip +0 -0
  136. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-bytes-per-file-limit.zip +0 -0
  137. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-device.zip +0 -0
  138. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-duplicate-entry.zip +0 -0
  139. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-fifo.zip +0 -0
  140. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-files-per-skill-limit.zip +0 -0
  141. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-nul-name.zip +0 -0
  142. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-parent-traversal.zip +0 -0
  143. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-reserved-marker.zip +0 -0
  144. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-socket.zip +0 -0
  145. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-symlink.zip +0 -0
  146. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-total-uncompressed-limit.zip +0 -0
  147. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/invalid-windows-drive-prefix.zip +0 -0
  148. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill-stored.zip +0 -0
  149. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/archives/valid-single-skill.zip +0 -0
  150. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/bundles/newer-than-plan.zip +0 -0
  151. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/bundles/normal.zip +0 -0
  152. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/catalog.json +0 -0
  153. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/corpus.sha256 +0 -0
  154. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/hashes/content-v1.json +0 -0
  155. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/hashes/manifest-v1.json +0 -0
  156. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-identity.json +0 -0
  157. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-duplicate-install-name.json +0 -0
  158. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/invalid-manifest-hash.json +0 -0
  159. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/newer-than-plan.json +0 -0
  160. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/manifests/normal-bundle.json +0 -0
  161. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/below-pro.json +0 -0
  162. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/cli-upgrade-required.json +0 -0
  163. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/lost-membership.json +0 -0
  164. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/normal-empty-plan.json +0 -0
  165. {gumloop-0.5.2 → gumloop-0.5.3}/tests/fixtures/skill_sync/v1/responses/normal-plan.json +0 -0
  166. {gumloop-0.5.2 → gumloop-0.5.3}/tests/integration/conftest.py +0 -0
  167. {gumloop-0.5.2 → gumloop-0.5.3}/tests/integration/test_live.py +0 -0
  168. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/__init__.py +0 -0
  169. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/conftest.py +0 -0
  170. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/helpers.py +0 -0
  171. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_agents.py +0 -0
  172. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_artifacts.py +0 -0
  173. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_brain.py +0 -0
  174. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_chat.py +0 -0
  175. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_client.py +0 -0
  176. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_errors.py +0 -0
  177. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_evaluations.py +0 -0
  178. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_gumcp_transport.py +0 -0
  179. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_mcp.py +0 -0
  180. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_models.py +0 -0
  181. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_oauth.py +0 -0
  182. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_organizations.py +0 -0
  183. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_sessions.py +0 -0
  184. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_skills.py +0 -0
  185. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_sync.py +0 -0
  186. {gumloop-0.5.2 → gumloop-0.5.3}/tests/sdk/test_teams.py +0 -0
  187. {gumloop-0.5.2 → gumloop-0.5.3}/tests/skill_sync_fixtures.py +0 -0
  188. {gumloop-0.5.2 → gumloop-0.5.3}/tests/test_client.py +0 -0
  189. {gumloop-0.5.2 → gumloop-0.5.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gumloop
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Python client for the Gumloop API
5
5
  Project-URL: Homepage, https://github.com/gumloop/gumloop-py
6
6
  Project-URL: Bug Tracker, https://github.com/gumloop/gumloop-py/issues
@@ -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 = (stream_base_url or _derive_stream_base_url(self.base_url)).rstrip("/")
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 = (stream_base_url or _derive_stream_base_url(self.base_url)).rstrip("/")
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)
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.5.2"
3
+ __version__ = "0.5.3"
@@ -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