marvin 2.3.0__tar.gz → 2.3.2__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 (283) hide show
  1. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/static-analysis.yml +0 -1
  2. {marvin-2.3.0 → marvin-2.3.2}/PKG-INFO +7 -7
  3. {marvin-2.3.0 → marvin-2.3.2}/README.md +1 -1
  4. {marvin-2.3.0 → marvin-2.3.2}/cookbook/azure/README.md +2 -2
  5. marvin-2.3.2/cookbook/gh_util/custom_assistant.py +20 -0
  6. marvin-2.3.2/cookbook/logfire/README.md +41 -0
  7. marvin-2.3.2/cookbook/logfire/auto_trace.py +7 -0
  8. marvin-2.3.2/cookbook/logfire/demo_app.py +79 -0
  9. marvin-2.3.2/cookbook/logfire/hello.py +40 -0
  10. marvin-2.3.2/cookbook/logfire/send_demo_request.py +47 -0
  11. marvin-2.3.2/cookbook/reasking.py +24 -0
  12. marvin-2.3.2/cookbook/slackbot/Dockerfile.slackbot +23 -0
  13. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/start.py +9 -12
  14. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/tools.py +7 -2
  15. marvin-2.3.2/cookbook/translate_schemas.py +18 -0
  16. marvin-2.3.2/docs/assets/images/docs/examples/logfire-span.jpeg +0 -0
  17. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/speech.md +36 -10
  18. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/images/generation.md +11 -0
  19. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/applications.md +1 -1
  20. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/assistants.md +40 -2
  21. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/cli.md +11 -1
  22. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/classification.md +67 -30
  23. {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/installation.md +15 -8
  24. {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/tutorial.md +1 -1
  25. {marvin-2.3.0 → marvin-2.3.2}/pyproject.toml +9 -13
  26. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/__init__.py +1 -1
  27. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/types.py +28 -7
  28. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_version.py +2 -2
  29. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/audio.py +55 -15
  30. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/images.py +36 -5
  31. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/text_prompts.py +1 -0
  32. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/text.py +53 -17
  33. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/audio.py +53 -10
  34. marvin-2.3.2/src/marvin/beta/ai_flow/ai_flow.py +43 -0
  35. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/ai_flow/ai_task.py +4 -4
  36. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/applications.py +2 -2
  37. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/planner.py +2 -1
  38. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/__init__.py +2 -2
  39. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/assistants.py +85 -20
  40. marvin-2.3.2/src/marvin/beta/assistants/formatting.py +271 -0
  41. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/runs.py +47 -38
  42. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/threads.py +18 -28
  43. marvin-2.3.2/src/marvin/beta/retries.py +43 -0
  44. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/vision.py +19 -8
  45. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/assistants.py +112 -7
  46. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/threads.py +23 -0
  47. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/client/openai.py +34 -16
  48. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/settings.py +31 -8
  49. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/assistants.py +6 -4
  50. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/code.py +2 -1
  51. marvin-2.3.2/src/marvin/tools/filesystem.py +268 -0
  52. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/types.py +89 -15
  53. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/asyncio.py +15 -12
  54. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/context.py +26 -7
  55. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/jinja.py +2 -0
  56. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/testing.py +36 -7
  57. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/tools.py +21 -5
  58. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/PKG-INFO +7 -7
  59. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/SOURCES.txt +12 -5
  60. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/requires.txt +5 -5
  61. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_cast.py +14 -41
  62. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_classify.py +8 -0
  63. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_extract.py +25 -47
  64. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_cast.py +2 -4
  65. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_classifier.py +1 -1
  66. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_classify.py +64 -7
  67. {marvin-2.3.0 → marvin-2.3.2}/tests/beta/assistants/test_assistants.py +15 -0
  68. marvin-2.3.0/tests/client/openai.py → marvin-2.3.2/tests/client/test_openai.py +22 -0
  69. marvin-2.3.2/tests/fixtures/__init__.py +2 -0
  70. marvin-2.3.2/tests/fixtures/assistants.py +13 -0
  71. marvin-2.3.0/cookbook/slackbot/Dockerfile.slackbot +0 -20
  72. marvin-2.3.0/src/marvin/beta/ai_flow/ai_flow.py +0 -48
  73. marvin-2.3.0/src/marvin/beta/assistants/formatting.py +0 -323
  74. marvin-2.3.0/src/marvin/beta/chat_ui/__init__.py +0 -1
  75. marvin-2.3.0/src/marvin/beta/chat_ui/chat_ui.py +0 -111
  76. marvin-2.3.0/src/marvin/beta/chat_ui/static/chat.html +0 -108
  77. marvin-2.3.0/src/marvin/beta/chat_ui/static/chat.js +0 -108
  78. marvin-2.3.0/src/marvin/tools/filesystem.py +0 -151
  79. marvin-2.3.0/tests/fixtures/__init__.py +0 -1
  80. {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/1_general_bug_report.yaml +0 -0
  81. {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/2_feature_request.yaml +0 -0
  82. {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/3_docs_change.yaml +0 -0
  83. {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/config.yaml +0 -0
  84. {marvin-2.3.0 → marvin-2.3.2}/.github/dependabot.yml +0 -0
  85. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/build-docs.yml +0 -0
  86. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/deploy-cloudrun-revision-community.yaml +0 -0
  87. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/deploy-cloudrun-revision-internal.yaml +0 -0
  88. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/image-build-and-push-community.yaml +0 -0
  89. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/image-build-and-push-internal.yaml +0 -0
  90. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/publish-docs.yml +0 -0
  91. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/publish-pypi.yml +0 -0
  92. {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/run-tests.yml +0 -0
  93. {marvin-2.3.0 → marvin-2.3.2}/.pre-commit-config.yaml +0 -0
  94. {marvin-2.3.0 → marvin-2.3.2}/LICENSE +0 -0
  95. {marvin-2.3.0 → marvin-2.3.2}/MANIFEST.in +0 -0
  96. {marvin-2.3.0 → marvin-2.3.2}/cookbook/__init__.py +0 -0
  97. {marvin-2.3.0 → marvin-2.3.2}/cookbook/_archive/docs_writer.py +0 -0
  98. {marvin-2.3.0 → marvin-2.3.2}/cookbook/_archive/test_writing_application.py +0 -0
  99. {marvin-2.3.0 → marvin-2.3.2}/cookbook/azure/usage.py +0 -0
  100. {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/github_digest/repo_activity.py +0 -0
  101. {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/github_digest/utils.py +0 -0
  102. {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/insurance_claim.py +0 -0
  103. {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/label_issues.py +0 -0
  104. {marvin-2.3.0 → marvin-2.3.2}/cookbook/maze.py +0 -0
  105. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/.dockerignore +0 -0
  106. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/README.md +0 -0
  107. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/keywords.py +0 -0
  108. {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/parent_app.py +0 -0
  109. {marvin-2.3.0 → marvin-2.3.2}/docs/CNAME +0 -0
  110. {marvin-2.3.0 → marvin-2.3.2}/docs/ai_style_guide.md +0 -0
  111. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/audio.md +0 -0
  112. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/images.md +0 -0
  113. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/text.md +0 -0
  114. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/applications.md +0 -0
  115. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/assistants.md +0 -0
  116. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/formatting.md +0 -0
  117. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/index.md +0 -0
  118. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/runs.md +0 -0
  119. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/threads.md +0 -0
  120. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants.md +0 -0
  121. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/vision.md +0 -0
  122. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/marvin.md +0 -0
  123. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/settings.md +0 -0
  124. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/types.md +0 -0
  125. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/asyncio.md +0 -0
  126. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/context.md +0 -0
  127. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/images.md +0 -0
  128. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/jinja.md +0 -0
  129. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/logging.md +0 -0
  130. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/openai.md +0 -0
  131. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/pydantic.md +0 -0
  132. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/python.md +0 -0
  133. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/slack.md +0 -0
  134. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/strings.md +0 -0
  135. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/testing.md +0 -0
  136. {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/tools.md +0 -0
  137. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/ai_say.mp3 +0 -0
  138. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/dont_panic.mp3 +0 -0
  139. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/fancy_computer.mp3 +0 -0
  140. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/hello_arthur.mp3 +0 -0
  141. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/hello_marvin.mp3 +0 -0
  142. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/this_is_a_test.mp3 +0 -0
  143. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/this_is_a_test_2.mp3 +0 -0
  144. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_1.png +0 -0
  145. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_2.png +0 -0
  146. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_3.png +0 -0
  147. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_conversation.png +0 -0
  148. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/advanced.png +0 -0
  149. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/arthur_marvin_example.png +0 -0
  150. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/code_interpreter.png +0 -0
  151. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/custom_tools.png +0 -0
  152. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/instructions.png +0 -0
  153. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/quickstart.png +0 -0
  154. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/sin_x.png +0 -0
  155. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/talking.png +0 -0
  156. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/upload_files.png +0 -0
  157. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/cli/hero.png +0 -0
  158. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/cli/tools.png +0 -0
  159. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/coffee.png +0 -0
  160. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/robot.png +0 -0
  161. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_scifi.png +0 -0
  162. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_summer.png +0 -0
  163. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_winter.png +0 -0
  164. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_0.png +0 -0
  165. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_025.png +0 -0
  166. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_075.png +0 -0
  167. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_1.png +0 -0
  168. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/three_cats.png +0 -0
  169. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/two_cats_airport.png +0 -0
  170. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/two_cats_airport_revision_0_1.png +0 -0
  171. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/vision/marvin.webp +0 -0
  172. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/ai_model.png +0 -0
  173. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/dont_panic.png +0 -0
  174. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/it_hates_me_hero.png +0 -0
  175. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/life_the_universe_and_ai.png +0 -0
  176. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/pip_install_marvin_hero.png +0 -0
  177. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/sort_of_thing_you_enjoy.png +0 -0
  178. {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/logos/askmarvin_mascot.jpeg +0 -0
  179. {marvin-2.3.0 → marvin-2.3.2}/docs/community/development_guide.md +0 -0
  180. {marvin-2.3.0 → marvin-2.3.2}/docs/community/feedback.md +0 -0
  181. {marvin-2.3.0 → marvin-2.3.2}/docs/community/index.md +0 -0
  182. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/recording.md +0 -0
  183. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/transcription.md +0 -0
  184. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/configuration/settings.md +0 -0
  185. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/extraction.md +0 -0
  186. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/functions.md +0 -0
  187. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/generation.md +0 -0
  188. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/transformation.md +0 -0
  189. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/video/recording.md +0 -0
  190. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/captioning.md +0 -0
  191. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/classification.md +0 -0
  192. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/extraction.md +0 -0
  193. {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/transformation.md +0 -0
  194. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/audio_modification.md +0 -0
  195. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/being_specific_about_types.md +0 -0
  196. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/call_routing.md +0 -0
  197. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/deduplication.md +0 -0
  198. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/github_digest.md +0 -0
  199. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/hogwarts_sorting_hat/hogwarts_patch.webp +0 -0
  200. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/hogwarts_sorting_hat/hogwarts_sorting_hat.md +0 -0
  201. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/michael_scott_business/michael_scott.jpg +0 -0
  202. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/michael_scott_business/michael_scott_business.md +0 -0
  203. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/python_augmented_prompts.md +0 -0
  204. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/slackbot.md +0 -0
  205. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/webcam_narration.md +0 -0
  206. {marvin-2.3.0 → marvin-2.3.2}/docs/examples/xkcd_bird.md +0 -0
  207. {marvin-2.3.0 → marvin-2.3.2}/docs/help/legacy_docs.md +0 -0
  208. {marvin-2.3.0 → marvin-2.3.2}/docs/hooks.py +0 -0
  209. {marvin-2.3.0 → marvin-2.3.2}/docs/index.md +0 -0
  210. {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/home.html +0 -0
  211. {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/footer.html +0 -0
  212. {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/grid.html +0 -0
  213. {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/toc-item.html +0 -0
  214. {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/tailwind.css +0 -0
  215. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/badges.css +0 -0
  216. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/custom.css +0 -0
  217. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/global.css +0 -0
  218. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/home.css +0 -0
  219. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/mkdocstrings.css +0 -0
  220. {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/tailwind.css +0 -0
  221. {marvin-2.3.0 → marvin-2.3.2}/docs/utilities (old)/chat_completion.md +0 -0
  222. {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/overview.md +0 -0
  223. {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/what_is_marvin.md +0 -0
  224. {marvin-2.3.0 → marvin-2.3.2}/mkdocs.insiders.yml +0 -0
  225. {marvin-2.3.0 → marvin-2.3.2}/mkdocs.yml +0 -0
  226. {marvin-2.3.0 → marvin-2.3.2}/prefect.yaml +0 -0
  227. {marvin-2.3.0 → marvin-2.3.2}/scripts/serve_legacy_docs +0 -0
  228. {marvin-2.3.0 → marvin-2.3.2}/setup.cfg +0 -0
  229. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/__init__.py +0 -0
  230. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/base_model.py +0 -0
  231. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/chat_completion.py +0 -0
  232. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/__init__.py +0 -0
  233. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/audio_prompts.py +0 -0
  234. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/image_prompts.py +0 -0
  235. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/vision_prompts.py +0 -0
  236. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/__init__.py +0 -0
  237. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/ai_flow/__init__.py +0 -0
  238. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/__init__.py +0 -0
  239. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/__init__.py +0 -0
  240. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/disk.py +0 -0
  241. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/json_block.py +0 -0
  242. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/state.py +0 -0
  243. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/handlers.py +0 -0
  244. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/__init__.py +0 -0
  245. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/client/__init__.py +0 -0
  246. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/__init__.py +0 -0
  247. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/chroma.py +0 -0
  248. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/github.py +0 -0
  249. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/__init__.py +0 -0
  250. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/images.py +0 -0
  251. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/logging.py +0 -0
  252. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/mapping.py +0 -0
  253. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/openai.py +0 -0
  254. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/pydantic.py +0 -0
  255. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/python.py +0 -0
  256. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/slack.py +0 -0
  257. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/strings.py +0 -0
  258. {marvin-2.3.0 → marvin-2.3.2}/src/marvin/video.py +0 -0
  259. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/dependency_links.txt +0 -0
  260. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/entry_points.txt +0 -0
  261. {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/top_level.txt +0 -0
  262. {marvin-2.3.0 → marvin-2.3.2}/tailwind.config.js +0 -0
  263. {marvin-2.3.0 → marvin-2.3.2}/tests/__init__.py +0 -0
  264. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/__init__.py +0 -0
  265. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/__init__.py +0 -0
  266. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/__init__.py +0 -0
  267. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_extract.py +0 -0
  268. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_functions.py +0 -0
  269. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_generate.py +0 -0
  270. {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_models.py +0 -0
  271. {marvin-2.3.0 → marvin-2.3.2}/tests/beta/__init__.py +0 -0
  272. {marvin-2.3.0 → marvin-2.3.2}/tests/beta/assistants/__init__.py +0 -0
  273. {marvin-2.3.0 → marvin-2.3.2}/tests/cli/__init__.py +0 -0
  274. {marvin-2.3.0 → marvin-2.3.2}/tests/cli/test_version.py +0 -0
  275. {marvin-2.3.0 → marvin-2.3.2}/tests/client/__init__.py +0 -0
  276. {marvin-2.3.0 → marvin-2.3.2}/tests/conftest.py +0 -0
  277. {marvin-2.3.0 → marvin-2.3.2}/tests/fixtures/llms.py +0 -0
  278. {marvin-2.3.0 → marvin-2.3.2}/tests/package_size.sh +0 -0
  279. {marvin-2.3.0 → marvin-2.3.2}/tests/test_settings.py +0 -0
  280. {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/__init__.py +0 -0
  281. {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/test_pydantic.py +0 -0
  282. {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/test_testing_utilities.py +0 -0
  283. {marvin-2.3.0 → marvin-2.3.2}/tests/utils.py +0 -0
@@ -15,7 +15,6 @@ permissions:
15
15
  contents: read
16
16
 
17
17
  jobs:
18
-
19
18
  static_analysis:
20
19
  timeout-minutes: 1
21
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: marvin
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: A lightweight AI engineering toolkit for building natural language interfaces that are reliable, scalable, and easy to trust.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -217,7 +217,8 @@ Requires-Dist: fastapi
217
217
  Requires-Dist: httpx>=0.24.1
218
218
  Requires-Dist: jinja2>=3.1.2
219
219
  Requires-Dist: jsonpatch>=1.33
220
- Requires-Dist: openai>=1.4.0
220
+ Requires-Dist: openai>=1.21.0
221
+ Requires-Dist: prompt-toolkit>=3.0.33
221
222
  Requires-Dist: pydantic>=2.4.2
222
223
  Requires-Dist: pydantic_settings
223
224
  Requires-Dist: rich>=12
@@ -248,6 +249,7 @@ Requires-Dist: pre-commit<4.0,>=2.21; extra == "dev"
248
249
  Requires-Dist: pydantic[dotenv]; extra == "dev"
249
250
  Requires-Dist: ruff; extra == "dev"
250
251
  Provides-Extra: tests
252
+ Requires-Dist: flaky; extra == "tests"
251
253
  Requires-Dist: pytest-asyncio!=0.22.0,<0.23.0,>=0.18.2; extra == "tests"
252
254
  Requires-Dist: pytest-env<2.0,>=0.8; extra == "tests"
253
255
  Requires-Dist: pytest-rerunfailures<14,>=10; extra == "tests"
@@ -258,9 +260,6 @@ Requires-Dist: pytest-xdist; extra == "tests"
258
260
  Provides-Extra: audio
259
261
  Requires-Dist: SpeechRecognition>=3.10; extra == "audio"
260
262
  Requires-Dist: PyAudio>=0.2.11; extra == "audio"
261
- Requires-Dist: playsound>=1.0; extra == "audio"
262
- Requires-Dist: wheel>=0.43.0; extra == "audio"
263
- Requires-Dist: PyObjC; extra == "audio"
264
263
  Requires-Dist: pydub>=0.25; extra == "audio"
265
264
  Requires-Dist: simpleaudio>=1.0; extra == "audio"
266
265
  Provides-Extra: video
@@ -268,7 +267,8 @@ Requires-Dist: opencv-python>=4.5; extra == "video"
268
267
  Provides-Extra: slackbot
269
268
  Requires-Dist: marvin[prefect]; extra == "slackbot"
270
269
  Requires-Dist: numpy; extra == "slackbot"
271
- Requires-Dist: marvin[chromadb]; extra == "slackbot"
270
+ Requires-Dist: raggy; extra == "slackbot"
271
+ Requires-Dist: turbopuffer; extra == "slackbot"
272
272
 
273
273
  <p align="center">
274
274
  <img src="docs/assets/images/heroes/it_hates_me_hero.png" style="width: 95%; height: auto;"/>
@@ -514,7 +514,7 @@ In addition to text, Marvin has beta support for captioning, classifying, transf
514
514
 
515
515
  ```python
516
516
  marvin.beta.classify(
517
- marvin.Image("docs/images/coffee.png"),
517
+ marvin.beta.Image("docs/images/coffee.png"),
518
518
  labels=["drink", "food"],
519
519
  )
520
520
 
@@ -242,7 +242,7 @@ In addition to text, Marvin has beta support for captioning, classifying, transf
242
242
 
243
243
  ```python
244
244
  marvin.beta.classify(
245
- marvin.Image("docs/images/coffee.png"),
245
+ marvin.beta.Image("docs/images/coffee.png"),
246
246
  labels=["drink", "food"],
247
247
  )
248
248
 
@@ -26,7 +26,7 @@ As an alternative to setting environment variables, you can pass the `AzureOpenA
26
26
 
27
27
  ```python
28
28
  import marvin
29
- from marvin.client import MarvinClient
29
+ from marvin.client import AsyncMarvinClient
30
30
 
31
31
  from openai import AzureOpenAI
32
32
 
@@ -37,7 +37,7 @@ azure_openai_client = AzureOpenAI(
37
37
  )
38
38
 
39
39
  @marvin.fn(
40
- client=MarvinClient(client=azure_openai_client),
40
+ client=AsyncMarvinClient(client=azure_openai_client),
41
41
  model_kwargs={"model": "your_deployment_name"}
42
42
  )
43
43
  def list_fruits(n: int) -> list[str]:
@@ -0,0 +1,20 @@
1
+ from gh_util.api import functions # pip install gh-util
2
+ from marvin.beta.applications import Application
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class Memory(BaseModel):
7
+ notes: list[str] = Field(default_factory=list)
8
+
9
+
10
+ octocat = Application(
11
+ name="octocat",
12
+ state=Memory(),
13
+ tools=[f for f in functions if f.__name__ != "run_git_command"],
14
+ )
15
+
16
+ # $ marvin assistant register cookbook/gh_util/custom_assistant.py:octocat
17
+
18
+ # > what's the latest release of prefecthq/marvin?
19
+
20
+ # see https://github.com/PrefectHQ/marvin/pull/875
@@ -0,0 +1,41 @@
1
+ ## using logfire with marvin
2
+
3
+ [logfire](https://github.com/pydantic/logfire?tab=readme-ov-file#pydantic-logfire--uncomplicated-observability) is brand new (Apr 2024) and is an observability tool for python applications - [otel](https://opentelemetry.io/docs/what-is-opentelemetry/)-based tracing, metrics, and logging. its pretty [awesome](https://docs.pydantic.dev/logfire/#pydantic-logfire-the-observability-platform-you-deserve).
4
+
5
+ they also happen to wrap OpenAI pretty well out of the box! see `hello.py` for a simple example.
6
+
7
+ ### setup
8
+ ```conosle
9
+ pip install marvin
10
+ ```
11
+ > [!NOTE]
12
+ > optionally, if you want to try out the fastapi integration
13
+ > ```console
14
+ > pip install 'logfire[fastapi]' uvicorn
15
+ > ```
16
+
17
+ login to logfire
18
+ ```console
19
+ logfire auth
20
+ ```
21
+
22
+ ### usage
23
+ use of marvin should be no different than any other library. check out [logfire's documentation](https://docs.pydantic.dev/logfire/#pydantic-logfire-the-observability-platform-you-deserve) for more information.
24
+
25
+
26
+ ### examples
27
+ ```console
28
+ gh repo clone prefecthq/marvin && cd marvin
29
+ uvicorn cookbook.logfire.demo_app:app
30
+ ```
31
+
32
+ in another terminal
33
+ ```console
34
+ python cookbook/logfire/send_demo_request.py
35
+ ```
36
+
37
+ check out the api docs at http://localhost:8000/docs or your logfire dashboard to see the traces and logs like:
38
+
39
+ <p align="center">
40
+ <img src="/docs/assets/images/docs/examples/logfire-span.jpeg" alt="logfire span"/>
41
+ </p>
@@ -0,0 +1,7 @@
1
+ import logfire
2
+
3
+ logfire.install_auto_tracing(modules=["hello"])
4
+
5
+ from hello import main # noqa
6
+
7
+ main()
@@ -0,0 +1,79 @@
1
+ from enum import Enum
2
+
3
+ import logfire
4
+ import openai
5
+ from fastapi import Body, FastAPI
6
+ from marvin import fn
7
+ from marvin.client import AsyncMarvinClient
8
+ from pydantic import BaseModel
9
+
10
+ app = FastAPI()
11
+ client = openai.AsyncClient()
12
+
13
+ logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record="all"))
14
+ logfire.instrument_openai(client)
15
+ logfire.instrument_fastapi(app)
16
+
17
+
18
+ class Seniority(Enum):
19
+ """ranked seniority levels for candidates"""
20
+
21
+ JUNIOR = 1
22
+ MID = 2
23
+ SENIOR = 3
24
+ STAFF = 4
25
+
26
+
27
+ class Candidate(BaseModel):
28
+ name: str
29
+ self_identified_seniority: Seniority
30
+ bio: str
31
+
32
+
33
+ class Role(BaseModel):
34
+ title: str
35
+ desired_seniority: Seniority
36
+ description: str
37
+
38
+
39
+ @fn(client=AsyncMarvinClient(client=client))
40
+ def choose_among_candidates(cohort: list[Candidate], role: Role) -> Candidate:
41
+ return (
42
+ f"We need a {role.desired_seniority.name} (at least) {role.title} that can "
43
+ f"most likely fulfill a job of this description:\n{role.description}\n"
44
+ )
45
+
46
+
47
+ @logfire.instrument("Dystopian Interview Process", extract_args=True)
48
+ def dystopian_interview_process(candidates: list[Candidate], role: Role) -> Candidate:
49
+ senior_enough_candidates = [
50
+ candidate
51
+ for candidate in candidates
52
+ if candidate.self_identified_seniority.value >= role.desired_seniority.value
53
+ ]
54
+ logfire.info(
55
+ "Candidates at or above {seniority} level: {cohort}",
56
+ cohort=[c.name for c in senior_enough_candidates],
57
+ seniority=role.desired_seniority,
58
+ )
59
+ if len(senior_enough_candidates) == 1:
60
+ return senior_enough_candidates[0]
61
+
62
+ with logfire.span("Choosing among candidates"):
63
+ return choose_among_candidates(senior_enough_candidates, role)
64
+
65
+
66
+ @app.post("/interview")
67
+ async def interview(
68
+ candidates: list[Candidate] = Body(..., description="List of candidates"),
69
+ role: Role = Body(..., description="Role to fill"),
70
+ ) -> Candidate:
71
+ best_candidate = dystopian_interview_process(candidates, role)
72
+ logfire.info("Best candidate: {best_candidate}", best_candidate=best_candidate)
73
+ return best_candidate
74
+
75
+
76
+ if __name__ == "__main__":
77
+ import uvicorn
78
+
79
+ uvicorn.run(app, host="localhost", port=8000)
@@ -0,0 +1,40 @@
1
+ """
2
+ Example of using logfire to instrument OpenAI API calls
3
+
4
+ see https://x.com/Nathan_Nowack/status/1785413529232708087
5
+ """
6
+
7
+ import logfire
8
+ import openai
9
+ from marvin import fn
10
+ from marvin.client import AsyncMarvinClient
11
+ from pydantic import BaseModel, Field
12
+
13
+ client = openai.AsyncClient()
14
+
15
+ logfire.instrument_openai(client)
16
+
17
+
18
+ class Ingredients(BaseModel):
19
+ name: str
20
+ approximate_price: float = Field(..., gt=0, description="Price in USD")
21
+ quantity: int
22
+
23
+
24
+ class Recipe(BaseModel):
25
+ ingredients: list[Ingredients]
26
+ steps: list[str]
27
+
28
+
29
+ @fn(client=AsyncMarvinClient(client=client))
30
+ def make_recipe(vibe: str) -> Recipe:
31
+ """Generate a recipe based on a vibe"""
32
+
33
+
34
+ def main():
35
+ recipe = make_recipe("italian, for 4 people")
36
+ assert isinstance(recipe, Recipe)
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,47 @@
1
+ import asyncio
2
+ import json
3
+
4
+ import httpx
5
+
6
+
7
+ async def main():
8
+ candidates = [
9
+ {
10
+ "name": "Alice",
11
+ "self_identified_seniority": 3,
12
+ "bio": "10 years with postgres, 5 years with python, 3 years with django.",
13
+ },
14
+ {
15
+ "name": "Bob",
16
+ "self_identified_seniority": 1,
17
+ "bio": "I just graduated from a coding bootcamp and I'm ready to take on the world!",
18
+ },
19
+ {
20
+ "name": "Charlie",
21
+ "self_identified_seniority": 2,
22
+ "bio": "graduated 2 years ago and i can make you a react app in no time",
23
+ },
24
+ {
25
+ "name": "David",
26
+ "self_identified_seniority": 3,
27
+ "bio": "i just been riding that SCRUM wave for 10 years fam",
28
+ },
29
+ ]
30
+
31
+ role = {
32
+ "title": "Senior Software Engineer",
33
+ "desired_seniority": 3,
34
+ "description": "Build and maintain a large-scale web application with a team of 10+ engineers.",
35
+ }
36
+
37
+ async with httpx.AsyncClient() as client:
38
+ response = await client.post(
39
+ "http://localhost:8000/interview",
40
+ json={"candidates": candidates, "role": role},
41
+ )
42
+ result = response.json()
43
+ print(json.dumps(result, indent=2))
44
+
45
+
46
+ if __name__ == "__main__":
47
+ asyncio.run(main())
@@ -0,0 +1,24 @@
1
+ from typing import Annotated
2
+
3
+ import marvin
4
+ from marvin.beta.retries import retry_fn_on_validation_error
5
+ from pydantic import AfterValidator
6
+
7
+
8
+ def verify_random_number(number: int) -> int:
9
+ if number != 37:
10
+ raise ValueError("Everyone knows the most random number is 37!")
11
+ return number
12
+
13
+
14
+ RandomNumber = Annotated[int, AfterValidator(verify_random_number)]
15
+
16
+
17
+ @retry_fn_on_validation_error # shows the validation error message in subsequent retries
18
+ @marvin.fn
19
+ def get_random_number() -> RandomNumber:
20
+ """returns a random number"""
21
+
22
+
23
+ if __name__ == "__main__":
24
+ print(get_random_number())
@@ -0,0 +1,23 @@
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY . /app
6
+
7
+ RUN apt-get update && \
8
+ apt-get install -y git build-essential curl && \
9
+ apt-get clean && \
10
+ rm -rf /var/lib/apt/lists/*
11
+
12
+ RUN pip install uv && uv venv
13
+
14
+ ENV VIRTUAL_ENV=/app/.venv
15
+ RUN uv venv
16
+
17
+ ENV PATH="$VIRTUAL_ENV/bin:$PATH"
18
+
19
+ RUN uv pip install ".[slackbot]"
20
+
21
+ EXPOSE 4200
22
+
23
+ CMD ["python", "cookbook/slackbot/start.py"]
@@ -1,13 +1,14 @@
1
1
  import asyncio
2
+ import os
2
3
  import re
3
4
 
5
+ import marvin
4
6
  import uvicorn
5
7
  from fastapi import FastAPI, HTTPException, Request
6
8
  from keywords import handle_keywords
7
9
  from marvin.beta.applications import Application
8
10
  from marvin.beta.applications.state.json_block import JSONBlockState
9
11
  from marvin.beta.assistants import Assistant, Thread
10
- from marvin.tools.chroma import store_document
11
12
  from marvin.tools.github import search_github_issues
12
13
  from marvin.utilities.logging import get_logger
13
14
  from marvin.utilities.slack import (
@@ -38,7 +39,7 @@ def get_feature_flag_value(flag_name: str) -> bool:
38
39
  ENABLE_PARENT_APP = get_feature_flag_value("enable-parent-app")
39
40
 
40
41
 
41
- @flow(name="Handle Slack Message")
42
+ @flow(name="Handle Slack Message", retries=1)
42
43
  async def handle_message(payload: SlackPayload) -> Completed:
43
44
  user_message = (event := payload.event).text
44
45
  cleaned_message = re.sub(BOT_MENTION, "", user_message).strip()
@@ -83,16 +84,6 @@ async def handle_message(payload: SlackPayload) -> Completed:
83
84
  )
84
85
  user_name, user_notes = (await get_notes_for_user(user_id=event.user)).popitem()
85
86
 
86
- task(store_document).submit(
87
- document=cleaned_message,
88
- metadata={
89
- "user": f"{user_name} ({event.user})",
90
- "user_notes": user_notes or "",
91
- "channel": await get_channel_name(event.channel),
92
- "thread": thread,
93
- },
94
- )
95
-
96
87
  with Assistant(
97
88
  name="Marvin",
98
89
  tools=[
@@ -114,6 +105,9 @@ async def handle_message(payload: SlackPayload) -> Completed:
114
105
  " prioritize brevity in your responses, and format text prettily for Slack."
115
106
  f"{ ('here are some notes on the user:' + user_notes) if user_notes else ''}"
116
107
  " ALWAYS provide links to the source of your information - let's think step-by-step."
108
+ " If a tool returns an irrelevant/bad result, you should try another tool."
109
+ " KEEP IN MIND that agents are deprecated in favor of workers, so you should"
110
+ " never recommend `prefect agent` commands, suggest `prefect worker` instead."
117
111
  ),
118
112
  ) as ai:
119
113
  logger.debug_kv(
@@ -197,4 +191,7 @@ async def chat_endpoint(request: Request):
197
191
 
198
192
 
199
193
  if __name__ == "__main__":
194
+ if not os.getenv("OPENAI_API_KEY", None): # TODO: Remove this
195
+ os.environ["OPENAI_API_KEY"] = marvin.settings.openai.api_key.get_secret_value()
196
+
200
197
  uvicorn.run(app, host="0.0.0.0", port=4200)
@@ -3,7 +3,9 @@ from typing import Literal
3
3
 
4
4
  import httpx
5
5
  import marvin
6
- from marvin.tools.chroma import multi_query_chroma
6
+ import turbopuffer as tpuf
7
+ from prefect.blocks.system import Secret
8
+ from raggy.vectorstores.tpuf import multi_query_tpuf
7
9
 
8
10
  Topic = Literal["latest_prefect_version"]
9
11
 
@@ -21,7 +23,10 @@ async def search_prefect_docs(queries: list[str]) -> str:
21
23
  - "retrieve run metadata dynamically"
22
24
 
23
25
  """
24
- return await multi_query_chroma(queries=queries, n_results=3)
26
+ if not tpuf.api_key:
27
+ tpuf.api_key = (await Secret.load("tpuf-api-key")).get()
28
+
29
+ return await multi_query_tpuf(queries, namespace="marvin-slackbot")
25
30
 
26
31
 
27
32
  async def get_latest_release_notes() -> str:
@@ -0,0 +1,18 @@
1
+ import marvin
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class FooModel(BaseModel):
6
+ identifier: str
7
+ name: str
8
+
9
+
10
+ class BarModel(BaseModel):
11
+ id: int
12
+ first_name: str
13
+ last_name: str
14
+
15
+
16
+ bar = marvin.cast(FooModel(identifier="42", name="Marvin Robot"), BarModel)
17
+
18
+ assert bar.model_dump() == {"id": 42, "first_name": "Marvin", "last_name": "Robot"}
@@ -24,7 +24,7 @@ Marvin can generate speech from text.
24
24
 
25
25
  !!! success "Result"
26
26
  ```python
27
- audio.stream_to_file("fancy_computer.mp3")
27
+ audio.play("fancy_computer.mp3")
28
28
  ```
29
29
  <audio controls>
30
30
  <source src="/assets/audio/fancy_computer.mp3" type="audio/mpeg">
@@ -47,7 +47,7 @@ Marvin can generate speech from text.
47
47
 
48
48
  !!! success "Result"
49
49
  ```python
50
- audio.stream_to_file("hello_arthur.mp3")
50
+ audio.play("hello_arthur.mp3")
51
51
  ```
52
52
  <audio controls>
53
53
  <source src="/assets/audio/hello_arthur.mp3" type="audio/mpeg">
@@ -61,11 +61,11 @@ Marvin can generate speech from text.
61
61
  </p>
62
62
  </div>
63
63
 
64
- ## Speaking text verbatim
64
+ !!! tip "Text is generated verbatim"
65
65
 
66
- Unlike the images API, OpenAI's speech API does not modify or revise your input prompt in any way. Whatever text you provide is exactly what will be spoken.
66
+ Unlike the images API, OpenAI's speech API does not modify or revise your input prompt in any way. Whatever text you provide is exactly what will be spoken.
67
67
 
68
- Therefore, you can use the `speak` function to generate speech from any string, or use the `@speech` decorator to generate speech from the string output of any function.
68
+ Therefore, you can use the `speak` function to generate speech from any string, or use the `@speech` decorator to generate speech from the string output of any function.
69
69
 
70
70
 
71
71
 
@@ -95,19 +95,45 @@ ai_say('hello')
95
95
  Your browser does not support the audio element.
96
96
  </audio>
97
97
 
98
- ## Choosing a voice
98
+ ### Playing audio
99
99
 
100
- Both `speak` and `@speech` accept a `voice` parameter that allows you to choose from a variety of voices. You can preview the available voices [here](https://platform.openai.com/docs/guides/text-to-speech/voice-options).
100
+ The result of `speak` and `@speech` is an `Audio` object that can be played by calling its `play` method. By default, playback will start as soon as the first bytes of audio are available. See the note on [streaming audio](#streaming-audio) for more information.
101
101
 
102
102
  ```python
103
+ audio = marvin.speak("Hello, world!")
104
+ audio.play()
105
+ ```
103
106
 
104
- ## Saving audio files
107
+ #### Streaming audio
108
+ By default, Marvin streams audio from the OpenAI API, which means that playback can start as soon as the first bytes of audio are available. This can be useful for long audio files, as it allows you to start listening to the audio before it has finished generating. If you want to wait for the entire audio file to be generated before starting playback, you can pass `stream=False`:
105
109
 
106
- The result of the `speak` function and `@speech` decorator is an audio stream. You can save this stream to disk like this:
110
+ ```python
111
+ audio = marvin.speak("Hello, world!", stream=False)
112
+ ```
113
+ Note that streaming is only supported with the `pcm` (or raw) audio file format, and an error will be raised if you try to generate speech in a different format with `stream=True`. However, you can always save `pcm` audio to a file in a different format after it has been generated.
114
+
115
+ ### Saving audio
116
+ To save an `Audio` object to a file, you can call its `save` method:
107
117
 
108
118
  ```python
109
119
  audio = marvin.speak("Hello, world!")
110
- audio.stream_to_file("hello_world.mp3")
120
+ audio.save("hello_world.mp3")
121
+ ```
122
+
123
+ Marvin will attempt to infer the correct file format from the file extension you provide. If you want to save the audio in a different format, you can pass a `format` argument to `save`.
124
+
125
+ ### Saving audio
126
+
127
+
128
+ ## Choosing a voice
129
+
130
+ Both `speak` and `@speech` accept a `voice` parameter that allows you to choose from a variety of voices. You can preview the available voices [here](https://platform.openai.com/docs/guides/text-to-speech/voice-options).
131
+
132
+ ```python
133
+ The result of the `speak` function and `@speech` decorator is an audio stream.
134
+
135
+ audio = marvin.speak("Hello, world!", voice="nova")
136
+ audio.play("hello_world.mp3")
111
137
  ```
112
138
 
113
139
 
@@ -50,6 +50,7 @@ Marvin can generate images from text.
50
50
  </p>
51
51
  </div>
52
52
 
53
+
53
54
  ## Generating images from functions
54
55
 
55
56
  In addition to passing prompts directly to the DALLE-3 API via the `paint` function, you can also use the `@image` decorator to generate images from the output of a function. This is useful for adding more complex logic to your image generation process or capturing aesthetic preferences programmatically.
@@ -279,3 +280,13 @@ marvin.utilities.images.base64_to_image(
279
280
  ```
280
281
 
281
282
  To change this behavior globally set `MARVIN_IMAGE_RESPONSE_FORMAT=b64_json` in your environment, or equivalently change `marvin.settings.images.response_format = "b64_json"` in your code.
283
+
284
+ ## Async support
285
+
286
+ If you are using Marvin in an async environment, you can use `paint_async`:
287
+
288
+ ```python
289
+ image = await marvin.paint_async(
290
+ "A cup of coffee, still warm"
291
+ )
292
+ ```
@@ -309,7 +309,7 @@ app = Application(
309
309
 
310
310
  ### Running the app
311
311
 
312
- Now we can interact with our app. After each command, you can print the resulting message to see the LLM's response, or use the experimental `app.chat()` interface to interact with the application in real time. You can also see the updated application state.
312
+ Now we can interact with our app. You can also see the updated application state.
313
313
 
314
314
  ```python
315
315
  response = app.say("I need to go to the store tomorrow afternoon")
@@ -105,7 +105,7 @@ For more control over the conversation, including posting multiple user messages
105
105
 
106
106
  #### Event handlers
107
107
 
108
- Marvin uses the OpenAI streaming API to provide real-time updates on the assistant's actions. To customize how these updates are handled, you can provide a custom event handler class to the `event_handler_class` parameter of `Assistant.say`, `Thread.run`, or `Run.run`. This class must inherit from `openai.AssistantEventHandler` or `openai.AsyncAssistantEventHandler`. For more control, you can also provide `event_handler_kwargs` that will be provided to the event handler when it is instantiated.
108
+ Marvin uses the OpenAI streaming API to provide real-time updates on the assistant's actions. To customize how these updates are handled, you can provide a custom event handler class to the `event_handler_class` parameter of `Assistant.say`, `Thread.run`, or `Run.run`. This class must inherit from `openai.AsyncAssistantEventHandler` (so all methods must be async). For more control, you can also provide `event_handler_kwargs` that will be provided to the event handler when it is instantiated.
109
109
 
110
110
  #### Pretty-printing
111
111
 
@@ -171,6 +171,9 @@ Each assistant can be given `instructions` that describe its purpose, personalit
171
171
  !!! success "Result"
172
172
  ![](/assets/images/docs/assistants/instructions.png)
173
173
 
174
+ Instructions are rendered as a Jinja template, which means you can use variables and conditionals to customize the assistant's behavior. A special variable, `self_` is provided to the template, which represents the assistant object itself. This allows you to template the assistant's name, tools, or other attributes into the instructions.
175
+
176
+
174
177
  ### Tools
175
178
 
176
179
  Each assistant can be given a list of `tools` that it can use when responding to a message. Tools are a way to extend the assistant's capabilities beyond its default behavior, including giving it access to external systems like the internet, a database, your computer, or any API.
@@ -225,6 +228,41 @@ Marvin makes it easy to give your assistants custom tools. To do so, pass one or
225
228
  !!! success "Result"
226
229
  ![](/assets/images/docs/assistants/custom_tools.png)
227
230
 
231
+ #### Ending a run early
232
+
233
+ Normally, the assistant will continue to run until it decides to stop, which usually happens after generating a response. Sometimes it may be useful to end a run early, for example if the assistant uses a tool that indicates the conversation is over. To do this, you can raise an `EndRun` exception from within a tool. This will cause the assistant to cancel the current run and return control. EndRun exceptions can contain data.
234
+
235
+ There are three ways to raise an `EndRun` exception:
236
+
237
+ 1. Raise the exception directly from the tool function:
238
+ ```python
239
+ from marvin.beta.assistants import Assistant, EndRun
240
+
241
+ def my_tool():
242
+ raise EndRun(data="The final result")
243
+
244
+ ai = Assistant(tools=[my_tool])
245
+ ```
246
+ 1. Return the exception from the tool function. This is useful if e.g. your tools are wrapped in custom exception handlers:
247
+ ```python
248
+ from marvin.beta.assistants import Assistant, EndRun
249
+
250
+ def my_tool():
251
+ return EndRun(data="The final result")
252
+
253
+ ai = Assistant(tools=[my_tool])
254
+ ```
255
+ 1. Return a special string value from the tool function. This is useful if you don't have full control over the tool itself, or need to ensure the tool output is JSON-compatible. Note that this approach does not allow you to attach any data to the exception:
256
+ ```python
257
+ from marvin.beta.assistants import Assistant, ENDRUN_TOKEN
258
+
259
+ def my_tool():
260
+ return ENDRUN_TOKEN
261
+
262
+ ai = Assistant(tools=[my_tool])
263
+ ```
264
+
265
+
228
266
  ### Lifecycle management
229
267
 
230
268
  Assistants are Marvin objects that correspond to remote objects in the OpenAI API. You can not communicate with an assistant unless it has been registered with the API.
@@ -384,7 +422,7 @@ This will return a `Run` object that represents the OpenAI run. You can use this
384
422
  When threads are `run` with an assistant, the same lifecycle management rules apply as when you use the assistant's `say` method. In the above example, lazy lifecycle management is used for conveneince. See [lifecycle management](#lifecycle-management) for more information.
385
423
 
386
424
  !!! warning "Threads are locked while running"
387
- When an assistant is running a thread, the thread is locked and no other messages can be added to it. This applies to both user and assistant messages.
425
+ When an assistant is running a thread, the thread is locked and no other messages can be added to it. This applies to both user and assistant messages. To end a run early, you must [use a custom tool](#ending-a-run-early).
388
426
 
389
427
  ### Reading messages
390
428