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.
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/static-analysis.yml +0 -1
- {marvin-2.3.0 → marvin-2.3.2}/PKG-INFO +7 -7
- {marvin-2.3.0 → marvin-2.3.2}/README.md +1 -1
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/azure/README.md +2 -2
- marvin-2.3.2/cookbook/gh_util/custom_assistant.py +20 -0
- marvin-2.3.2/cookbook/logfire/README.md +41 -0
- marvin-2.3.2/cookbook/logfire/auto_trace.py +7 -0
- marvin-2.3.2/cookbook/logfire/demo_app.py +79 -0
- marvin-2.3.2/cookbook/logfire/hello.py +40 -0
- marvin-2.3.2/cookbook/logfire/send_demo_request.py +47 -0
- marvin-2.3.2/cookbook/reasking.py +24 -0
- marvin-2.3.2/cookbook/slackbot/Dockerfile.slackbot +23 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/start.py +9 -12
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/tools.py +7 -2
- marvin-2.3.2/cookbook/translate_schemas.py +18 -0
- marvin-2.3.2/docs/assets/images/docs/examples/logfire-span.jpeg +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/speech.md +36 -10
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/images/generation.md +11 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/applications.md +1 -1
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/assistants.md +40 -2
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/interactive/cli.md +11 -1
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/classification.md +67 -30
- {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/installation.md +15 -8
- {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/tutorial.md +1 -1
- {marvin-2.3.0 → marvin-2.3.2}/pyproject.toml +9 -13
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/__init__.py +1 -1
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/types.py +28 -7
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_version.py +2 -2
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/audio.py +55 -15
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/images.py +36 -5
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/text_prompts.py +1 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/text.py +53 -17
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/audio.py +53 -10
- marvin-2.3.2/src/marvin/beta/ai_flow/ai_flow.py +43 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/ai_flow/ai_task.py +4 -4
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/applications.py +2 -2
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/planner.py +2 -1
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/__init__.py +2 -2
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/assistants.py +85 -20
- marvin-2.3.2/src/marvin/beta/assistants/formatting.py +271 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/runs.py +47 -38
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/threads.py +18 -28
- marvin-2.3.2/src/marvin/beta/retries.py +43 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/vision.py +19 -8
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/assistants.py +112 -7
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/threads.py +23 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/client/openai.py +34 -16
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/settings.py +31 -8
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/assistants.py +6 -4
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/code.py +2 -1
- marvin-2.3.2/src/marvin/tools/filesystem.py +268 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/types.py +89 -15
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/asyncio.py +15 -12
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/context.py +26 -7
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/jinja.py +2 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/testing.py +36 -7
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/tools.py +21 -5
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/PKG-INFO +7 -7
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/SOURCES.txt +12 -5
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/requires.txt +5 -5
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_cast.py +14 -41
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_classify.py +8 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/test_extract.py +25 -47
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_cast.py +2 -4
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_classifier.py +1 -1
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_classify.py +64 -7
- {marvin-2.3.0 → marvin-2.3.2}/tests/beta/assistants/test_assistants.py +15 -0
- marvin-2.3.0/tests/client/openai.py → marvin-2.3.2/tests/client/test_openai.py +22 -0
- marvin-2.3.2/tests/fixtures/__init__.py +2 -0
- marvin-2.3.2/tests/fixtures/assistants.py +13 -0
- marvin-2.3.0/cookbook/slackbot/Dockerfile.slackbot +0 -20
- marvin-2.3.0/src/marvin/beta/ai_flow/ai_flow.py +0 -48
- marvin-2.3.0/src/marvin/beta/assistants/formatting.py +0 -323
- marvin-2.3.0/src/marvin/beta/chat_ui/__init__.py +0 -1
- marvin-2.3.0/src/marvin/beta/chat_ui/chat_ui.py +0 -111
- marvin-2.3.0/src/marvin/beta/chat_ui/static/chat.html +0 -108
- marvin-2.3.0/src/marvin/beta/chat_ui/static/chat.js +0 -108
- marvin-2.3.0/src/marvin/tools/filesystem.py +0 -151
- marvin-2.3.0/tests/fixtures/__init__.py +0 -1
- {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/1_general_bug_report.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/2_feature_request.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/3_docs_change.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/ISSUE_TEMPLATE/config.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/dependabot.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/build-docs.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/deploy-cloudrun-revision-community.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/deploy-cloudrun-revision-internal.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/image-build-and-push-community.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/image-build-and-push-internal.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/publish-docs.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/publish-pypi.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.github/workflows/run-tests.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/.pre-commit-config.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/LICENSE +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/MANIFEST.in +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/_archive/docs_writer.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/_archive/test_writing_application.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/azure/usage.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/github_digest/repo_activity.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/github_digest/utils.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/insurance_claim.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/flows/label_issues.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/maze.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/.dockerignore +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/README.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/keywords.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/cookbook/slackbot/parent_app.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/CNAME +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/ai_style_guide.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/audio.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/images.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/ai/text.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/applications.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/assistants.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/formatting.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/index.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/runs.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants/threads.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/assistants.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/beta/vision.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/marvin.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/settings.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/types.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/asyncio.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/context.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/images.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/jinja.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/logging.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/openai.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/pydantic.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/python.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/slack.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/strings.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/testing.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/api_reference/utilities/tools.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/ai_say.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/dont_panic.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/fancy_computer.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/hello_arthur.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/hello_marvin.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/this_is_a_test.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/audio/this_is_a_test_2.mp3 +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_1.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_2.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_3.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/applications/todo_conversation.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/advanced.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/arthur_marvin_example.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/code_interpreter.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/custom_tools.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/instructions.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/quickstart.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/sin_x.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/talking.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/assistants/upload_files.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/cli/hero.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/cli/tools.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/coffee.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/robot.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_scifi.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_summer.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/sunset_winter.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_0.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_025.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_075.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/teacup_revision_1.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/three_cats.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/two_cats_airport.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/images/two_cats_airport_revision_0_1.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/docs/vision/marvin.webp +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/ai_model.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/dont_panic.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/it_hates_me_hero.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/life_the_universe_and_ai.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/pip_install_marvin_hero.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/heroes/sort_of_thing_you_enjoy.png +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/assets/images/logos/askmarvin_mascot.jpeg +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/community/development_guide.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/community/feedback.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/community/index.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/recording.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/audio/transcription.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/configuration/settings.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/extraction.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/functions.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/generation.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/text/transformation.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/video/recording.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/captioning.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/classification.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/extraction.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/docs/vision/transformation.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/audio_modification.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/being_specific_about_types.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/call_routing.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/deduplication.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/github_digest.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/hogwarts_sorting_hat/hogwarts_patch.webp +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/hogwarts_sorting_hat/hogwarts_sorting_hat.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/michael_scott_business/michael_scott.jpg +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/michael_scott_business/michael_scott_business.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/python_augmented_prompts.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/slackbot.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/webcam_narration.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/examples/xkcd_bird.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/help/legacy_docs.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/hooks.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/index.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/home.html +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/footer.html +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/grid.html +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/partials/toc-item.html +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/overrides/tailwind.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/badges.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/custom.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/global.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/home.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/mkdocstrings.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/static/css/tailwind.css +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/utilities (old)/chat_completion.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/overview.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/docs/welcome/what_is_marvin.md +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/mkdocs.insiders.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/mkdocs.yml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/prefect.yaml +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/scripts/serve_legacy_docs +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/setup.cfg +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/base_model.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/_mappings/chat_completion.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/audio_prompts.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/image_prompts.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/ai/prompts/vision_prompts.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/ai_flow/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/disk.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/json_block.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/applications/state/state.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/beta/assistants/handlers.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/cli/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/client/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/chroma.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/tools/github.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/images.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/logging.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/mapping.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/openai.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/pydantic.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/python.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/slack.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/utilities/strings.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin/video.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/dependency_links.txt +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/entry_points.txt +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/src/marvin.egg-info/top_level.txt +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tailwind.config.js +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/beta/vision/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_extract.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_functions.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_generate.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/ai/test_models.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/beta/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/beta/assistants/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/cli/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/cli/test_version.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/client/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/conftest.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/fixtures/llms.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/package_size.sh +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/test_settings.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/__init__.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/test_pydantic.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/utilities/test_testing_utilities.py +0 -0
- {marvin-2.3.0 → marvin-2.3.2}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: marvin
|
|
3
|
-
Version: 2.3.
|
|
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.
|
|
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:
|
|
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
|
|
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=
|
|
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,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
|
-
|
|
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
|
-
|
|
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"}
|
|
Binary file
|
|
@@ -24,7 +24,7 @@ Marvin can generate speech from text.
|
|
|
24
24
|
|
|
25
25
|
!!! success "Result"
|
|
26
26
|
```python
|
|
27
|
-
audio.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
98
|
+
### Playing audio
|
|
99
99
|
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|

|
|
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
|

|
|
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
|
|