Repository: stanfordnlp/dspy
Pull request #8479 (2025-07-02 01:45:08)

Fix native function calling in adapters
A few changes:

- In the base adapter, we need to explicitly set the fields to None if they don't present in the response for consistency. This happens often with native function calling, because either the content or tool_calls will be missing.
- In the JSONAdapter, we fall back to json mode if native function calling is not used while ToolCalls are in the output fields. We do this because structured output performs very poorly on fields with type like `dict[str, Any]`, which is the `args` in `dspy.ToolCalls`.
- Add unit tests to cover the native function calling response parsing. 


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5e1d02ad..4cc863a1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -89,7 +89,7 @@ Once the pull request is approved, a team member will take care of merging.
 
 ## Environment Setup
 
-Python 3.9 or later is required.
+Python 3.10 or later is required.
 
 Setting up your DSPy development environment requires you to fork the DSPy repository and clone it locally.
 If you are not familiar with the GitHub fork process, please refer to [Fork a repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo). After creating the fork, clone
diff --git a/README.md b/README.md
index 8bd03a3c..7dfa6af1 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
 
 **Documentation:** [DSPy Docs](https://dspy.ai/)
 
-[![PyPI Downloads](https://static.pepy.tech/badge/dspy/month)](https://pepy.tech/projects/dspy)
+![PyPI Downloads](https://img.shields.io/pypi/dm/dspy)
 
 
 ----
diff --git a/docs/docs/api/adapters/Adapter.md b/docs/docs/api/adapters/Adapter.md
index 3d266633..7fa2fd96 100644
--- a/docs/docs/api/adapters/Adapter.md
+++ b/docs/docs/api/adapters/Adapter.md
@@ -24,4 +24,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/adapters/ChatAdapter.md b/docs/docs/api/adapters/ChatAdapter.md
index e76eeec0..fa5e6a4d 100644
--- a/docs/docs/api/adapters/ChatAdapter.md
+++ b/docs/docs/api/adapters/ChatAdapter.md
@@ -27,4 +27,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/adapters/JSONAdapter.md b/docs/docs/api/adapters/JSONAdapter.md
index 0043c95e..718264b6 100644
--- a/docs/docs/api/adapters/JSONAdapter.md
+++ b/docs/docs/api/adapters/JSONAdapter.md
@@ -27,4 +27,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/adapters/TwoStepAdapter.md b/docs/docs/api/adapters/TwoStepAdapter.md
index aea44e4b..ec14b471 100644
--- a/docs/docs/api/adapters/TwoStepAdapter.md
+++ b/docs/docs/api/adapters/TwoStepAdapter.md
@@ -24,4 +24,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/evaluation/CompleteAndGrounded.md b/docs/docs/api/evaluation/CompleteAndGrounded.md
index 5c3c4728..20bdb1d4 100644
--- a/docs/docs/api/evaluation/CompleteAndGrounded.md
+++ b/docs/docs/api/evaluation/CompleteAndGrounded.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/evaluation/Evaluate.md b/docs/docs/api/evaluation/Evaluate.md
index 17d369d7..2c7c4451 100644
--- a/docs/docs/api/evaluation/Evaluate.md
+++ b/docs/docs/api/evaluation/Evaluate.md
@@ -14,4 +14,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/evaluation/SemanticF1.md b/docs/docs/api/evaluation/SemanticF1.md
index aa419648..537a657d 100644
--- a/docs/docs/api/evaluation/SemanticF1.md
+++ b/docs/docs/api/evaluation/SemanticF1.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/evaluation/answer_exact_match.md b/docs/docs/api/evaluation/answer_exact_match.md
index f3faa5f6..3ee48387 100644
--- a/docs/docs/api/evaluation/answer_exact_match.md
+++ b/docs/docs/api/evaluation/answer_exact_match.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/evaluation/answer_passage_match.md b/docs/docs/api/evaluation/answer_passage_match.md
index 2fcbaf52..7a571bcf 100644
--- a/docs/docs/api/evaluation/answer_passage_match.md
+++ b/docs/docs/api/evaluation/answer_passage_match.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/models/Embedder.md b/docs/docs/api/models/Embedder.md
index 48a2459a..510a749a 100644
--- a/docs/docs/api/models/Embedder.md
+++ b/docs/docs/api/models/Embedder.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/models/LM.md b/docs/docs/api/models/LM.md
index cb9a7442..20429c00 100644
--- a/docs/docs/api/models/LM.md
+++ b/docs/docs/api/models/LM.md
@@ -26,4 +26,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/BestOfN.md b/docs/docs/api/modules/BestOfN.md
index 225efff9..ae778a6c 100644
--- a/docs/docs/api/modules/BestOfN.md
+++ b/docs/docs/api/modules/BestOfN.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/ChainOfThought.md b/docs/docs/api/modules/ChainOfThought.md
index 21a1fad9..978ffae3 100644
--- a/docs/docs/api/modules/ChainOfThought.md
+++ b/docs/docs/api/modules/ChainOfThought.md
@@ -33,4 +33,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/Module.md b/docs/docs/api/modules/Module.md
index dbce6fb2..c192fc70 100644
--- a/docs/docs/api/modules/Module.md
+++ b/docs/docs/api/modules/Module.md
@@ -31,4 +31,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/MultiChainComparison.md b/docs/docs/api/modules/MultiChainComparison.md
index 90f0a950..364b6632 100644
--- a/docs/docs/api/modules/MultiChainComparison.md
+++ b/docs/docs/api/modules/MultiChainComparison.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/Parallel.md b/docs/docs/api/modules/Parallel.md
index f935c2db..5c798527 100644
--- a/docs/docs/api/modules/Parallel.md
+++ b/docs/docs/api/modules/Parallel.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/Predict.md b/docs/docs/api/modules/Predict.md
index 161ff372..6ae87aa1 100644
--- a/docs/docs/api/modules/Predict.md
+++ b/docs/docs/api/modules/Predict.md
@@ -36,4 +36,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/ProgramOfThought.md b/docs/docs/api/modules/ProgramOfThought.md
index 91efe719..b8e76b50 100644
--- a/docs/docs/api/modules/ProgramOfThought.md
+++ b/docs/docs/api/modules/ProgramOfThought.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/ReAct.md b/docs/docs/api/modules/ReAct.md
index b06dcf92..bf108aac 100644
--- a/docs/docs/api/modules/ReAct.md
+++ b/docs/docs/api/modules/ReAct.md
@@ -34,4 +34,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/modules/Refine.md b/docs/docs/api/modules/Refine.md
index 152b1fcc..19d7f759 100644
--- a/docs/docs/api/modules/Refine.md
+++ b/docs/docs/api/modules/Refine.md
@@ -32,4 +32,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/BetterTogether.md b/docs/docs/api/optimizers/BetterTogether.md
index 43063924..33029b47 100644
--- a/docs/docs/api/optimizers/BetterTogether.md
+++ b/docs/docs/api/optimizers/BetterTogether.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/BootstrapFewShot.md b/docs/docs/api/optimizers/BootstrapFewShot.md
index 3dace819..a02c14a6 100644
--- a/docs/docs/api/optimizers/BootstrapFewShot.md
+++ b/docs/docs/api/optimizers/BootstrapFewShot.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/BootstrapFewShotWithRandomSearch.md b/docs/docs/api/optimizers/BootstrapFewShotWithRandomSearch.md
index f2df1530..24593f4a 100644
--- a/docs/docs/api/optimizers/BootstrapFewShotWithRandomSearch.md
+++ b/docs/docs/api/optimizers/BootstrapFewShotWithRandomSearch.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/BootstrapFinetune.md b/docs/docs/api/optimizers/BootstrapFinetune.md
index 8f1d3a33..7e149fa1 100644
--- a/docs/docs/api/optimizers/BootstrapFinetune.md
+++ b/docs/docs/api/optimizers/BootstrapFinetune.md
@@ -17,4 +17,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/BootstrapRS.md b/docs/docs/api/optimizers/BootstrapRS.md
index df523b4e..013356ed 100644
--- a/docs/docs/api/optimizers/BootstrapRS.md
+++ b/docs/docs/api/optimizers/BootstrapRS.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/COPRO.md b/docs/docs/api/optimizers/COPRO.md
index 89926b40..9f92e68c 100644
--- a/docs/docs/api/optimizers/COPRO.md
+++ b/docs/docs/api/optimizers/COPRO.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/Ensemble.md b/docs/docs/api/optimizers/Ensemble.md
index 58aef770..9cd5e00e 100644
--- a/docs/docs/api/optimizers/Ensemble.md
+++ b/docs/docs/api/optimizers/Ensemble.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/InferRules.md b/docs/docs/api/optimizers/InferRules.md
index 035c0b13..b8af99a2 100644
--- a/docs/docs/api/optimizers/InferRules.md
+++ b/docs/docs/api/optimizers/InferRules.md
@@ -20,4 +20,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/KNN.md b/docs/docs/api/optimizers/KNN.md
index d8c3fcf9..dcf4c7a2 100644
--- a/docs/docs/api/optimizers/KNN.md
+++ b/docs/docs/api/optimizers/KNN.md
@@ -14,4 +14,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/KNNFewShot.md b/docs/docs/api/optimizers/KNNFewShot.md
index 758a6f81..edcc54ea 100644
--- a/docs/docs/api/optimizers/KNNFewShot.md
+++ b/docs/docs/api/optimizers/KNNFewShot.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/LabeledFewShot.md b/docs/docs/api/optimizers/LabeledFewShot.md
index f1d96313..55d7773c 100644
--- a/docs/docs/api/optimizers/LabeledFewShot.md
+++ b/docs/docs/api/optimizers/LabeledFewShot.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/optimizers/MIPROv2.md b/docs/docs/api/optimizers/MIPROv2.md
index 34a6f2ea..c8669e9e 100644
--- a/docs/docs/api/optimizers/MIPROv2.md
+++ b/docs/docs/api/optimizers/MIPROv2.md
@@ -17,6 +17,7 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
 
 ## Example Usage
diff --git a/docs/docs/api/primitives/Example.md b/docs/docs/api/primitives/Example.md
index 1cfecea0..b312c581 100644
--- a/docs/docs/api/primitives/Example.md
+++ b/docs/docs/api/primitives/Example.md
@@ -23,4 +23,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/primitives/History.md b/docs/docs/api/primitives/History.md
index 3b5b717d..1be9232b 100644
--- a/docs/docs/api/primitives/History.md
+++ b/docs/docs/api/primitives/History.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/primitives/Image.md b/docs/docs/api/primitives/Image.md
index 463937c2..c9029842 100644
--- a/docs/docs/api/primitives/Image.md
+++ b/docs/docs/api/primitives/Image.md
@@ -21,4 +21,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/primitives/Prediction.md b/docs/docs/api/primitives/Prediction.md
index 9f78b2b2..9060950f 100644
--- a/docs/docs/api/primitives/Prediction.md
+++ b/docs/docs/api/primitives/Prediction.md
@@ -26,4 +26,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/primitives/Tool.md b/docs/docs/api/primitives/Tool.md
index aa71ba2e..441e8bca 100644
--- a/docs/docs/api/primitives/Tool.md
+++ b/docs/docs/api/primitives/Tool.md
@@ -22,4 +22,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/signatures/InputField.md b/docs/docs/api/signatures/InputField.md
index f9b679d7..534b3740 100644
--- a/docs/docs/api/signatures/InputField.md
+++ b/docs/docs/api/signatures/InputField.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/signatures/OutputField.md b/docs/docs/api/signatures/OutputField.md
index 16a096ca..ca9f4858 100644
--- a/docs/docs/api/signatures/OutputField.md
+++ b/docs/docs/api/signatures/OutputField.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/signatures/Signature.md b/docs/docs/api/signatures/Signature.md
index 14e7db8a..7272acd5 100644
--- a/docs/docs/api/signatures/Signature.md
+++ b/docs/docs/api/signatures/Signature.md
@@ -22,4 +22,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/tools/ColBERTv2.md b/docs/docs/api/tools/ColBERTv2.md
index 9cfc1327..1743b07a 100644
--- a/docs/docs/api/tools/ColBERTv2.md
+++ b/docs/docs/api/tools/ColBERTv2.md
@@ -14,4 +14,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/tools/Embeddings.md b/docs/docs/api/tools/Embeddings.md
index ca1939a6..36b5553e 100644
--- a/docs/docs/api/tools/Embeddings.md
+++ b/docs/docs/api/tools/Embeddings.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/tools/PythonInterpreter.md b/docs/docs/api/tools/PythonInterpreter.md
index baf6af1c..bfc304d0 100644
--- a/docs/docs/api/tools/PythonInterpreter.md
+++ b/docs/docs/api/tools/PythonInterpreter.md
@@ -16,4 +16,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/StatusMessage.md b/docs/docs/api/utils/StatusMessage.md
index 161b7a13..cab18fb3 100644
--- a/docs/docs/api/utils/StatusMessage.md
+++ b/docs/docs/api/utils/StatusMessage.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/StatusMessageProvider.md b/docs/docs/api/utils/StatusMessageProvider.md
index 19fac23a..e22db0dd 100644
--- a/docs/docs/api/utils/StatusMessageProvider.md
+++ b/docs/docs/api/utils/StatusMessageProvider.md
@@ -19,4 +19,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/StreamListener.md b/docs/docs/api/utils/StreamListener.md
index 5785c6f1..517c398f 100644
--- a/docs/docs/api/utils/StreamListener.md
+++ b/docs/docs/api/utils/StreamListener.md
@@ -15,4 +15,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/asyncify.md b/docs/docs/api/utils/asyncify.md
index b1fa70a8..e42be0aa 100644
--- a/docs/docs/api/utils/asyncify.md
+++ b/docs/docs/api/utils/asyncify.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/configure_cache.md b/docs/docs/api/utils/configure_cache.md
index 87516a94..a13ae7b4 100644
--- a/docs/docs/api/utils/configure_cache.md
+++ b/docs/docs/api/utils/configure_cache.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/disable_litellm_logging.md b/docs/docs/api/utils/disable_litellm_logging.md
index 2d7b2a07..6280e796 100644
--- a/docs/docs/api/utils/disable_litellm_logging.md
+++ b/docs/docs/api/utils/disable_litellm_logging.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/disable_logging.md b/docs/docs/api/utils/disable_logging.md
index 6bb9debb..f2517325 100644
--- a/docs/docs/api/utils/disable_logging.md
+++ b/docs/docs/api/utils/disable_logging.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/enable_litellm_logging.md b/docs/docs/api/utils/enable_litellm_logging.md
index fa7f7370..3bd6f89d 100644
--- a/docs/docs/api/utils/enable_litellm_logging.md
+++ b/docs/docs/api/utils/enable_litellm_logging.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/enable_logging.md b/docs/docs/api/utils/enable_logging.md
index 55ae12b0..5fd3e772 100644
--- a/docs/docs/api/utils/enable_logging.md
+++ b/docs/docs/api/utils/enable_logging.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/inspect_history.md b/docs/docs/api/utils/inspect_history.md
index 81357c6d..1de813af 100644
--- a/docs/docs/api/utils/inspect_history.md
+++ b/docs/docs/api/utils/inspect_history.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/load.md b/docs/docs/api/utils/load.md
index dec1eaa5..00fdd2c5 100644
--- a/docs/docs/api/utils/load.md
+++ b/docs/docs/api/utils/load.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/api/utils/streamify.md b/docs/docs/api/utils/streamify.md
index 57e1f7ed..22fb7951 100644
--- a/docs/docs/api/utils/streamify.md
+++ b/docs/docs/api/utils/streamify.md
@@ -12,4 +12,5 @@
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
diff --git a/docs/docs/faqs.md b/docs/docs/faqs.md
index c75063ae..3a5616fe 100644
--- a/docs/docs/faqs.md
+++ b/docs/docs/faqs.md
@@ -19,7 +19,7 @@ The **DSPy** philosophy and abstraction differ significantly from other librarie
 
 ## Basic Usage
 
-**How should I use DSPy for my task?** We wrote a [eight-step guide](/building-blocks/solving_your_task) on this. In short, using DSPy is an iterative process. You first define your task and the metrics you want to maximize, and prepare a few example inputs — typically without labels (or only with labels for the final outputs, if your metric requires them). Then, you build your pipeline by selecting built-in layers (`modules`) to use, giving each layer a `signature` (input/output spec), and then calling your modules freely in your Python code. Lastly, you use a DSPy `optimizer` to compile your code into high-quality instructions, automatic few-shot examples, or updated LM weights for your LM.
+**How should I use DSPy for my task?** We wrote a [eight-step guide](learn/index.md) on this. In short, using DSPy is an iterative process. You first define your task and the metrics you want to maximize, and prepare a few example inputs — typically without labels (or only with labels for the final outputs, if your metric requires them). Then, you build your pipeline by selecting built-in layers (`modules`) to use, giving each layer a `signature` (input/output spec), and then calling your modules freely in your Python code. Lastly, you use a DSPy `optimizer` to compile your code into high-quality instructions, automatic few-shot examples, or updated LM weights for your LM.
 
 **How do I convert my complex prompt into a DSPy pipeline?** See the same answer above.
 
@@ -34,11 +34,11 @@ You can specify multiple output fields. For the short-form signature, you can li
 
 - **How do I define my own metrics? Can metrics return a float?**
 
-You can define metrics as simply Python functions that process model generations and evaluate them based on user-defined requirements. Metrics can compare existent data (e.g. gold labels) to model predictions or they can be used to assess various components of an output using validation feedback from LMs (e.g. LLMs-as-Judges). Metrics can return `bool`, `int`, and `float` types scores. Check out the official [Metrics docs](/building-blocks/5-metrics) to learn more about defining custom metrics and advanced evaluations using AI feedback and/or DSPy programs.
+You can define metrics as simply Python functions that process model generations and evaluate them based on user-defined requirements. Metrics can compare existent data (e.g. gold labels) to model predictions or they can be used to assess various components of an output using validation feedback from LMs (e.g. LLMs-as-Judges). Metrics can return `bool`, `int`, and `float` types scores. Check out the official [Metrics docs](learn/evaluation/metrics.md) to learn more about defining custom metrics and advanced evaluations using AI feedback and/or DSPy programs.
 
 - **How expensive or slow is compiling??**
 
-To reflect compiling metrics, we highlight an experiment for reference, compiling the [`SimplifiedBaleen`](/tutorials/simplified-baleen) using the [`dspy.BootstrapFewShotWithRandomSearch`](/deep-dive/teleprompter/bootstrap-fewshot) optimizer on the `gpt-3.5-turbo-1106` model over 7 candidate programs and 10 threads. We report that compiling this program takes around 6 minutes with 3200 API calls, 2.7 million input tokens and 156,000 output tokens, reporting a total cost of $3 USD (at the current pricing of the OpenAI model).
+To reflect compiling metrics, we highlight an experiment for reference, compiling a program using the [BootstrapFewShotWithRandomSearch](api/optimizers/BootstrapFewShotWithRandomSearch.md) optimizer on the `gpt-3.5-turbo-1106` model over 7 candidate programs and 10 threads. We report that compiling this program takes around 6 minutes with 3200 API calls, 2.7 million input tokens and 156,000 output tokens, reporting a total cost of $3 USD (at the current pricing of the OpenAI model).
 
 Compiling DSPy `optimizers` naturally will incur additional LM calls, but we substantiate this overhead with minimalistic executions with the goal of maximizing performance. This invites avenues to enhance performance of smaller models by compiling DSPy programs with larger models to learn enhanced behavior during compile-time and propagate such behavior to the tested smaller model during inference-time.  
 
diff --git a/docs/docs/index.md b/docs/docs/index.md
index 4c5ff98c..943f8367 100644
--- a/docs/docs/index.md
+++ b/docs/docs/index.md
@@ -10,7 +10,7 @@ hide:
 
 # _Programming_—not prompting—_LMs_
 
-[![PyPI Downloads](https://static.pepy.tech/badge/dspy/month)](https://pepy.tech/projects/dspy)
+![PyPI Downloads](https://img.shields.io/pypi/dm/dspy)
 
 DSPy is a declarative framework for building modular AI software. It allows you to **iterate fast on structured code**, rather than brittle strings, and offers algorithms that **compile AI programs into effective prompts and weights** for your language models, whether you're building simple classifiers, sophisticated RAG pipelines, or Agent loops.
 
@@ -374,7 +374,7 @@ Given a few tens or hundreds of representative _inputs_ of your task and a _metr
         optimized_rag = tp.compile(RAG(), trainset=trainset, max_bootstrapped_demos=2, max_labeled_demos=2)
         ```
 
-        For a complete RAG example that you can run, start this [tutorial](/tutorials/rag/). It improves the quality of a RAG system over a subset of StackExchange communities by 10% relative gain.
+        For a complete RAG example that you can run, start this [tutorial](tutorials/rag/index.ipynb). It improves the quality of a RAG system over a subset of StackExchange communities by 10% relative gain.
 
     === "Optimizing weights for Classification"
         <details><summary>Click to show dataset setup code.</summary>
@@ -447,4 +447,4 @@ Compared to monolithic LMs, DSPy's modular paradigm enables a large community to
 
 The DSPy research effort started at Stanford NLP in Feb 2022, building on what we had learned from developing early [compound LM systems](https://bair.berkeley.edu/blog/2024/02/18/compound-ai-systems/) like [ColBERT-QA](https://arxiv.org/abs/2007.00814), [Baleen](https://arxiv.org/abs/2101.00436), and [Hindsight](https://arxiv.org/abs/2110.07752). The first version was released as [DSP](https://arxiv.org/abs/2212.14024) in Dec 2022 and evolved by Oct 2023 into [DSPy](https://arxiv.org/abs/2310.03714). Thanks to [250 contributors](https://github.com/stanfordnlp/dspy/graphs/contributors), DSPy has introduced tens of thousands of people to building and optimizing modular LM programs.
 
-Since then, DSPy's community has produced a large body of work on optimizers, like [MIPROv2](https://arxiv.org/abs/2406.11695), [BetterTogether](https://arxiv.org/abs/2407.10930), and [LeReT](https://arxiv.org/abs/2410.23214), on program architectures, like [STORM](https://arxiv.org/abs/2402.14207), [IReRa](https://arxiv.org/abs/2401.12178), and [DSPy Assertions](https://arxiv.org/abs/2312.13382), and on successful applications to new problems, like [PAPILLON](https://arxiv.org/abs/2410.17127), [PATH](https://arxiv.org/abs/2406.11706), [WangLab@MEDIQA](https://arxiv.org/abs/2404.14544), [UMD's Prompting Case Study](https://arxiv.org/abs/2406.06608), and [Haize's Red-Teaming Program](https://blog.haizelabs.com/posts/dspy/), in addition to many open-source projects, production applications, and other [use cases](/community/use-cases).
+Since then, DSPy's community has produced a large body of work on optimizers, like [MIPROv2](https://arxiv.org/abs/2406.11695), [BetterTogether](https://arxiv.org/abs/2407.10930), and [LeReT](https://arxiv.org/abs/2410.23214), on program architectures, like [STORM](https://arxiv.org/abs/2402.14207), [IReRa](https://arxiv.org/abs/2401.12178), and [DSPy Assertions](https://arxiv.org/abs/2312.13382), and on successful applications to new problems, like [PAPILLON](https://arxiv.org/abs/2410.17127), [PATH](https://arxiv.org/abs/2406.11706), [WangLab@MEDIQA](https://arxiv.org/abs/2404.14544), [UMD's Prompting Case Study](https://arxiv.org/abs/2406.06608), and [Haize's Red-Teaming Program](https://blog.haizelabs.com/posts/dspy/), in addition to many open-source projects, production applications, and other [use cases](community/use-cases.md).
diff --git a/docs/docs/learn/optimization/optimizers.md b/docs/docs/learn/optimization/optimizers.md
index cb428c81..6cf9ffe6 100644
--- a/docs/docs/learn/optimization/optimizers.md
+++ b/docs/docs/learn/optimization/optimizers.md
@@ -41,34 +41,34 @@ Optimizers can be accessed via `from dspy.teleprompt import *`.
 
 These optimizers extend the signature by automatically generating and including **optimized** examples within the prompt sent to the model, implementing few-shot learning.
 
-1. [**`LabeledFewShot`**](/api/optimizers/LabeledFewShot): Simply constructs few-shot examples (demos) from provided labeled input and output data points.  Requires `k` (number of examples for the prompt) and `trainset` to randomly select `k` examples from.
+1. [**`LabeledFewShot`**](../../api/optimizers/LabeledFewShot.md): Simply constructs few-shot examples (demos) from provided labeled input and output data points.  Requires `k` (number of examples for the prompt) and `trainset` to randomly select `k` examples from.
 
-2. [**`BootstrapFewShot`**](/api/optimizers/BootstrapFewshot): Uses a `teacher` module (which defaults to your program) to generate complete demonstrations for every stage of your program, along with labeled examples in `trainset`. Parameters include `max_labeled_demos` (the number of demonstrations randomly selected from the `trainset`) and `max_bootstrapped_demos` (the number of additional examples generated by the `teacher`). The bootstrapping process employs the metric to validate demonstrations, including only those that pass the metric in the "compiled" prompt. Advanced: Supports using a `teacher` program that is a *different* DSPy program that has compatible structure, for harder tasks.
+2. [**`BootstrapFewShot`**](../../api/optimizers/BootstrapFewShot.md): Uses a `teacher` module (which defaults to your program) to generate complete demonstrations for every stage of your program, along with labeled examples in `trainset`. Parameters include `max_labeled_demos` (the number of demonstrations randomly selected from the `trainset`) and `max_bootstrapped_demos` (the number of additional examples generated by the `teacher`). The bootstrapping process employs the metric to validate demonstrations, including only those that pass the metric in the "compiled" prompt. Advanced: Supports using a `teacher` program that is a *different* DSPy program that has compatible structure, for harder tasks.
 
-3. [**`BootstrapFewShotWithRandomSearch`**](/api/optimizers/BootstrapFewshotWithRandomSearch): Applies `BootstrapFewShot` several times with random search over generated demonstrations, and selects the best program over the optimization. Parameters mirror those of `BootstrapFewShot`, with the addition of `num_candidate_programs`, which specifies the number of random programs evaluated over the optimization, including candidates of the uncompiled program, `LabeledFewShot` optimized program, `BootstrapFewShot` compiled program with unshuffled examples and `num_candidate_programs` of `BootstrapFewShot` compiled programs with randomized example sets.
+3. [**`BootstrapFewShotWithRandomSearch`**](../../api/optimizers/BootstrapFewShotWithRandomSearch.md): Applies `BootstrapFewShot` several times with random search over generated demonstrations, and selects the best program over the optimization. Parameters mirror those of `BootstrapFewShot`, with the addition of `num_candidate_programs`, which specifies the number of random programs evaluated over the optimization, including candidates of the uncompiled program, `LabeledFewShot` optimized program, `BootstrapFewShot` compiled program with unshuffled examples and `num_candidate_programs` of `BootstrapFewShot` compiled programs with randomized example sets.
 
-4. [**`KNNFewShot`**](/api/optimizers/KNNFewShot/). Uses k-Nearest Neighbors algorithm to find the nearest training example demonstrations for a given input example. These nearest neighbor demonstrations are then used as the trainset for the BootstrapFewShot optimization process.
+4. [**`KNNFewShot`**](../../api/optimizers/KNNFewShot.md). Uses k-Nearest Neighbors algorithm to find the nearest training example demonstrations for a given input example. These nearest neighbor demonstrations are then used as the trainset for the BootstrapFewShot optimization process.
 
 
 ### Automatic Instruction Optimization
 
 These optimizers produce optimal instructions for the prompt and, in the case of MIPROv2 can also optimize the set of few-shot demonstrations.
 
-5. [**`COPRO`**](/api/optimizers/COPRO): Generates and refines new instructions for each step, and optimizes them with coordinate ascent (hill-climbing using the metric function and the `trainset`). Parameters include `depth` which is the number of iterations of prompt improvement the optimizer runs over.
+5. [**`COPRO`**](../../api/optimizers/COPRO.md): Generates and refines new instructions for each step, and optimizes them with coordinate ascent (hill-climbing using the metric function and the `trainset`). Parameters include `depth` which is the number of iterations of prompt improvement the optimizer runs over.
 
-6. [**`MIPROv2`**](/api/optimizers/MIPROv2): Generates instructions *and* few-shot examples in each step. The instruction generation is data-aware and demonstration-aware. Uses Bayesian Optimization to effectively search over the space of generation instructions/demonstrations across your modules.
+6. [**`MIPROv2`**](../../api/optimizers/MIPROv2.md): Generates instructions *and* few-shot examples in each step. The instruction generation is data-aware and demonstration-aware. Uses Bayesian Optimization to effectively search over the space of generation instructions/demonstrations across your modules.
 
 
 ### Automatic Finetuning
 
 This optimizer is used to fine-tune the underlying LLM(s).
 
-7. [**`BootstrapFinetune`**](/api/optimizers/BootstrapFinetune): Distills a prompt-based DSPy program into weight updates. The output is a DSPy program that has the same steps, but where each step is conducted by a finetuned model instead of a prompted LM.
+7. [**`BootstrapFinetune`**](../../api/optimizers/BootstrapFinetune.md): Distills a prompt-based DSPy program into weight updates. The output is a DSPy program that has the same steps, but where each step is conducted by a finetuned model instead of a prompted LM.
 
 
 ### Program Transformations
 
-8. [**`Ensemble`**](/api/optimizers/Ensemble): Ensembles a set of DSPy programs and either uses the full set or randomly samples a subset into a single program.
+8. [**`Ensemble`**](../../api/optimizers/Ensemble.md): Ensembles a set of DSPy programs and either uses the full set or randomly samples a subset into a single program.
 
 
 ## Which optimizer should I use?
@@ -79,13 +79,13 @@ That being said, here's the general guidance on getting started:
 
 - If you have **very few examples** (around 10), start with `BootstrapFewShot`.
 - If you have **more data** (50 examples or more), try  `BootstrapFewShotWithRandomSearch`.
-- If you prefer to do **instruction optimization only** (i.e. you want to keep your prompt 0-shot), use `MIPROv2` [configured for 0-shot optimization to optimize](/api/optimizers/MIPROv2#optimizing-instructions-only-with-miprov2-0-shot). 
+- If you prefer to do **instruction optimization only** (i.e. you want to keep your prompt 0-shot), use `MIPROv2` [configured for 0-shot optimization](../../api/optimizers/MIPROv2.md). 
 - If you’re willing to use more inference calls to perform **longer optimization runs** (e.g. 40 trials or more), and have enough data (e.g. 200 examples or more to prevent overfitting) then try `MIPROv2`. 
 - If you have been able to use one of these with a large LM (e.g., 7B parameters or above) and need a very **efficient program**, finetune a small LM for your task with `BootstrapFinetune`.
 
 ## How do I use an optimizer?
 
-They all share this general interface, with some differences in the keyword arguments (hyperparameters). Detailed documentation for key optimizers can be found [here](/api/optimizers/vfrs), and a full list can be found [here](https://dspy.ai/api/optimizers/BetterTogether/).
+They all share this general interface, with some differences in the keyword arguments (hyperparameters). A full list can be found in the [API reference](../../api/optimizers/BetterTogether.md).
 
 Let's see this with the most common one, `BootstrapFewShotWithRandomSearch`.
 
@@ -147,7 +147,7 @@ optimized_program = teleprompter.compile(YOUR_PROGRAM_HERE, trainset=YOUR_TRAINS
         optimized_rag = tp.compile(RAG(), trainset=trainset, max_bootstrapped_demos=2, max_labeled_demos=2)
         ```
 
-        For a complete RAG example that you can run, start this [tutorial](/tutorials/rag/). It improves the quality of a RAG system over a subset of StackExchange communities from 53% to 61%.
+        For a complete RAG example that you can run, start this [tutorial](../../tutorials/rag/index.ipynb). It improves the quality of a RAG system over a subset of StackExchange communities from 53% to 61%.
 
     === "Optimizing weights for Classification"
         <details><summary>Click to show dataset setup code.</summary>
diff --git a/docs/docs/learn/programming/language_models.md b/docs/docs/learn/programming/language_models.md
index 8a000563..38592af9 100644
--- a/docs/docs/learn/programming/language_models.md
+++ b/docs/docs/learn/programming/language_models.md
@@ -152,7 +152,7 @@ with dspy.context(lm=dspy.LM('openai/gpt-3.5-turbo')):
 ```
 **Possible Output:**
 ```text
-GPT-4o: The number of floors in the castle David Gregory inherited cannot be determined with the information provided.
+GPT-4o-mini: The number of floors in the castle David Gregory inherited cannot be determined with the information provided.
 GPT-3.5-turbo: The castle David Gregory inherited has 7 floors.
 ```
 
diff --git a/docs/docs/learn/programming/modules.md b/docs/docs/learn/programming/modules.md
index a52f2bc4..2f3fff2d 100644
--- a/docs/docs/learn/programming/modules.md
+++ b/docs/docs/learn/programming/modules.md
@@ -15,7 +15,7 @@ A **DSPy module** is a building block for programs that use LMs.
 
 ## How do I use a built-in module, like `dspy.Predict` or `dspy.ChainOfThought`?
 
-Let's start with the most fundamental module, `dspy.Predict`. Internally, all other DSPy modules are built using `dspy.Predict`. We'll assume you are already at least a little familiar with [DSPy signatures](/learn/programming/signatures), which are declarative specs for defining the behavior of any module we use in DSPy.
+Let's start with the most fundamental module, `dspy.Predict`. Internally, all other DSPy modules are built using `dspy.Predict`. We'll assume you are already at least a little familiar with [DSPy signatures](signatures.md), which are declarative specs for defining the behavior of any module we use in DSPy.
 
 To use a module, we first **declare** it by giving it a signature. Then we **call** the module with the input arguments, and extract the output fields!
 
diff --git a/docs/docs/production/index.md b/docs/docs/production/index.md
index 0aa80065..bd8d6c64 100644
--- a/docs/docs/production/index.md
+++ b/docs/docs/production/index.md
@@ -8,7 +8,7 @@
 
     DSPy is deployed in production by many enterprises and startups. Explore real-world case studies.
 
-    [:octicons-arrow-right-24: Use Cases](../community/use-cases)
+    [:octicons-arrow-right-24: Use Cases](../community/use-cases.md)
 
 - :material-magnify-expand:{ .lg .middle } __Monitoring & Observability__
 
@@ -16,7 +16,7 @@
 
     Monitor your DSPy programs using **MLflow Tracing**, based on OpenTelemetry.
 
-    [:octicons-arrow-right-24: Set Up Observability](../tutorials/observability/#tracing)
+    [:octicons-arrow-right-24: Set Up Observability](../tutorials/observability/index.md#tracing)
 
 - :material-ab-testing: __Reproducibility__
 
@@ -32,7 +32,7 @@
 
     When it's time to productionize, deploy your application easily with DSPy's integration with MLflow Model Serving.
 
-    [:octicons-arrow-right-24: Deployment Guide](../tutorials/deployment)
+    [:octicons-arrow-right-24: Deployment Guide](../tutorials/deployment/index.md)
 
 - :material-arrow-up-right-bold: __Scalability__
 
diff --git a/docs/docs/roadmap.md b/docs/docs/roadmap.md
index cf39fda6..209ba55d 100644
--- a/docs/docs/roadmap.md
+++ b/docs/docs/roadmap.md
@@ -111,7 +111,7 @@ Over the next six months, our goal is to dramatically improve each angle of thes
 
 Using DSPy well for solving a new task is just doing good machine learning with LMs, but teaching this is hard. On the one hand, it's an iterative process: you make some initial choices, which will be sub-optimal, and then you refine them incrementally. It's highly exploratory: it's often the case that no one knows yet how to best solve a problem in a DSPy-esque way. One the other hand, DSPy offers many emerging lessons from several years of building LM systems, in which the design space, the data regime, and many other factors are new both to ML experts and to the very large fraction of users that have no ML experience.
 
-Though current docs do address [a bunch of this](/building-blocks/solving_your_task) in isolated ways, one thing we've learned is that we should separate teaching the core DSPy language (which is ultimately pretty small) from teaching the emerging ML workflow that works well in a DSPy-esque setting. As a natural extension of this, we need to place more emphasis on steps prior and after to the explicit coding in DSPy, from data collection to deployment that serves and monitors the optimized DSPy program in practice. This is just starting but efforts will be ramping up led by Omar Khattab, Isaac Miller, and Herumb Shandilya.
+Though current docs do address [a bunch of this](learn/index.md) in isolated ways, one thing we've learned is that we should separate teaching the core DSPy language (which is ultimately pretty small) from teaching the emerging ML workflow that works well in a DSPy-esque setting. As a natural extension of this, we need to place more emphasis on steps prior and after to the explicit coding in DSPy, from data collection to deployment that serves and monitors the optimized DSPy program in practice. This is just starting but efforts will be ramping up led by Omar Khattab, Isaac Miller, and Herumb Shandilya.
 
 
 ## 4) Shifting towards more interactive optimization & tracking.
diff --git a/docs/docs/tutorials/build_ai_program/index.md b/docs/docs/tutorials/build_ai_program/index.md
index 729b9fd1..53dd0d28 100644
--- a/docs/docs/tutorials/build_ai_program/index.md
+++ b/docs/docs/tutorials/build_ai_program/index.md
@@ -1,11 +1,46 @@
-- [Building AI Agents with DSPy](/tutorials/customer_service_agent/)
-- [Building AI Applications by Customizing DSPy Modules](/tutorials/custom_module/)
-- [Retrieval-Augmented Generation (RAG)](/tutorials/rag/)
-- [Building RAG as Agent](/tutorials/agents/)
-- [Entity Extraction](/tutorials/entity_extraction/)
-- [Classification](/tutorials/classification/)
-- [Multi-Hop RAG](/tutorials/multihop_search/)
-- [Privacy-Conscious Delegation](/tutorials/papillon/)
-- [Program Of Thought](/tutorials/program_of_thought/)
-- [Image Generation Prompt iteration](/tutorials/image_generation_prompting/)
-- [Audio](/tutorials/audio/)
+# Build AI Programs with DSPy
+
+This section contains hands-on tutorials that guide you through building production-ready AI applications using DSPy. Each tutorial demonstrates practical use cases and shows you how to leverage DSPy's modular programming approach to create robust, maintainable AI systems.
+
+## Core Applications
+
+### [Building AI Agents with DSPy](../customer_service_agent/index.ipynb)
+Learn to create intelligent agents that can handle complex customer service scenarios. This tutorial shows how to build agents that can understand context, maintain conversation state, and provide helpful responses.
+
+### [Building AI Applications by Customizing DSPy Modules](../custom_module/index.ipynb)
+Discover how to create custom DSPy modules tailored to your specific needs. Learn the patterns for building reusable, composable components that can be shared across different applications.
+
+## Retrieval-Augmented Generation (RAG)
+
+### [Retrieval-Augmented Generation (RAG)](../rag/index.ipynb)
+Master the fundamentals of RAG systems with DSPy. Learn how to combine retrieval mechanisms with language models to build systems that can answer questions using external knowledge sources.
+
+### [Building RAG as Agent](../agents/index.ipynb)
+Take RAG to the next level by building `ReAct` agent-based systems that can reason about when and how to retrieve information, making your RAG systems more intelligent and adaptive.
+
+### [Multi-Hop RAG](../multihop_search/index.ipynb)
+Build sophisticated RAG systems that can perform multi-step reasoning across multiple information sources, perfect for complex research and analysis tasks.
+
+## Specialized Use Cases
+
+### [Entity Extraction](../entity_extraction/index.ipynb)
+Learn to build systems that can identify and extract specific entities from text, essential for information processing and data analysis applications.
+
+### [Classification](../classification/index.md)
+Build robust text classification systems using DSPy's modular approach with a topic classification example.
+
+### [Privacy-Conscious Delegation](../papillon/index.md)
+Explore advanced techniques for building AI systems that respect privacy constraints while maintaining high performance by combining a small local model and an advanced external model.
+
+## Advanced Reasoning
+
+### [Program Of Thought](../program_of_thought/index.ipynb)
+Learn to build systems that can generate and execute code to solve complex problems, combining the power of language models with programmatic reasoning.
+
+## Multimodal Applications
+
+### [Image Generation Prompt iteration](../image_generation_prompting/index.ipynb)
+Discover how to use DSPy to iteratively improve image generation prompts, creating better visual content through systematic optimization.
+
+### [Audio](../audio/index.ipynb)
+Explore audio processing applications with DSPy, learning to build systems that can understand, process, and generate audio content.
diff --git a/docs/docs/tutorials/core_development/index.md b/docs/docs/tutorials/core_development/index.md
index 906cd0cf..6736ccd8 100644
--- a/docs/docs/tutorials/core_development/index.md
+++ b/docs/docs/tutorials/core_development/index.md
@@ -1,9 +1,38 @@
-- [Use MCP in DSPy](/tutorials/mcp/)
-- [Output Refinement](/tutorials/output_refinement/best-of-n-and-refine/)
-- [Saving and Loading](/tutorials/saving/)
-- [Cache](/tutorials/cache/)
-- [Deployment](/tutorials/deployment/)
-- [Debugging & Observability](/tutorials/observability/)
-- [Tracking DSPy Optimizers](/tutorials/optimizer_tracking/)
-- [Streaming](/tutorials/streaming/)
-- [Async](/tutorials/async/)
\ No newline at end of file
+# Tools, Development, and Deployment
+
+This section covers essential DSPy features and best practices for professional AI development. Learn how to implement key functionalities like streaming, caching, deployment, and monitoring in your DSPy applications. These tutorials focus on the practical aspects of building production-ready systems.
+
+## Integration and Tooling
+
+### [Use MCP in DSPy](../mcp/index.md)
+Learn to integrate Model Context Protocol (MCP) with DSPy applications. This tutorial shows how to leverage MCP for enhanced context management and more sophisticated AI interactions.
+
+### [Output Refinement](../output_refinement/best-of-n-and-refine.md)
+Master techniques for improving output quality through refinement strategies. Learn how to implement best-of-N sampling and iterative refinement to get higher-quality results from your DSPy programs.
+
+## Data Management and Persistence
+
+### [Saving and Loading](../saving/index.md)
+Understand how to persist and restore DSPy programs and their optimized states. Learn best practices for model versioning, checkpoint management, and program serialization.
+
+### [Cache](../cache/index.md)
+Implement efficient caching strategies to improve performance and reduce API costs. Learn how to configure and use DSPy's caching mechanisms effectively in different scenarios.
+
+## Production Deployment
+
+### [Deployment](../deployment/index.md)
+Learn to deploy DSPy applications in production environments. This tutorial covers multiple deployment strategies such as FastAPI and MLflow.
+
+### [Streaming](../streaming/index.md)
+Implement real-time streaming capabilities in your DSPy applications. Learn how to handle streaming responses for better user experience in interactive applications.
+
+### [Async](../async/index.md)
+Build asynchronous DSPy applications for improved performance and scalability. Learn async/await patterns and concurrent execution strategies for high-throughput systems.
+
+## Monitoring and Optimization
+
+### [Debugging & Observability](../observability/index.md)
+Master debugging and monitoring techniques for DSPy applications. Learn to use comprehensive logging, tracing, and error handling for production systems.
+
+### [Tracking DSPy Optimizers](../optimizer_tracking/index.md)
+Learn to track and analyze optimizer performance and behavior. Understand how to monitor optimization processes and enhance the reproducibility of the optimization.
diff --git a/docs/docs/tutorials/deployment/index.md b/docs/docs/tutorials/deployment/index.md
index a8527a6f..bbc7d77a 100644
--- a/docs/docs/tutorials/deployment/index.md
+++ b/docs/docs/tutorials/deployment/index.md
@@ -65,7 +65,7 @@ You can configure the async capacity using the new `async_max_workers` setting.
 
 ??? "Streaming, in DSPy 2.6.0+"
 
-    Streaming is also supported in DSPy 2.6.0+, available as a release candidate via `pip install -U --pre dspy`.
+    Streaming is also supported in DSPy 2.6.0+, which can be installed via `pip install -U dspy`.
 
     We can use `dspy.streamify` to convert the dspy program to a streaming mode. This is useful when you want to stream
     the intermediate outputs (i.e. O1-style reasoning) to the client before the final prediction is ready. This uses
diff --git a/docs/docs/tutorials/games/index.ipynb b/docs/docs/tutorials/games/index.ipynb
index 83d70e4d..e2d31b9e 100644
--- a/docs/docs/tutorials/games/index.ipynb
+++ b/docs/docs/tutorials/games/index.ipynb
@@ -10,7 +10,7 @@
     "\n",
     "### Install dependencies and download data\n",
     "\n",
-    "Install the latest DSPy via `pip install -U --pre dspy` and follow along. This tutorial uses the AlfWorld dataset, which depends on DSPy 2.6.0 (pre-release).\n",
+    "Install the latest DSPy via `pip install -U dspy` and follow along. This tutorial uses the AlfWorld dataset, which depends on DSPy 2.6.0.\n",
     "\n",
     "You will also need the following dependencies:\n",
     "\n",
diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md
index f253c924..90e0d226 100644
--- a/docs/docs/tutorials/index.md
+++ b/docs/docs/tutorials/index.md
@@ -13,34 +13,34 @@ Welcome to DSPy tutorials! We've organized our tutorials into three main categor
 
 
 - Build AI Programs with DSPy
-    - [Building AI Agents with DSPy](/tutorials/customer_service_agent/)
-    - [Building AI Applications by Customizing DSPy Modules](/tutorials/custom_module/)
-    - [Retrieval-Augmented Generation (RAG)](/tutorials/rag/)
-    - [Building RAG as Agent](/tutorials/agents/)
-    - [Entity Extraction](/tutorials/entity_extraction/)
-    - [Classification](/tutorials/classification/)
-    - [Multi-Hop RAG](/tutorials/multihop_search/)
-    - [Privacy-Conscious Delegation](/tutorials/papillon/)
-    - [Program Of Thought](/tutorials/program_of_thought/)
-    - [Image Generation Prompt iteration](/tutorials/image_generation_prompting/)
-    - [Audio](/tutorials/audio/)
+    - [Building AI Agents with DSPy](customer_service_agent/index.ipynb)
+    - [Building AI Applications by Customizing DSPy Modules](custom_module/index.ipynb)
+    - [Retrieval-Augmented Generation (RAG)](rag/index.ipynb)
+    - [Building RAG as Agent](agents/index.ipynb)
+    - [Entity Extraction](entity_extraction/index.ipynb)
+    - [Classification](classification/index.md)
+    - [Multi-Hop RAG](multihop_search/index.ipynb)
+    - [Privacy-Conscious Delegation](papillon/index.md)
+    - [Program Of Thought](program_of_thought/index.ipynb)
+    - [Image Generation Prompt iteration](image_generation_prompting/index.ipynb)
+    - [Audio](audio/index.ipynb)
 
 
 - Optimize AI Programs with DSPy
-    - [Math Reasoning](/tutorials/math/)
-    - [Classification Finetuning](/tutorials/classification_finetuning/)
-    - [Advanced Tool Use](/tutorials/tool_use/)
-    - [Finetuning Agents](/tutorials/games/)
+    - [Math Reasoning](math/index.ipynb)
+    - [Classification Finetuning](classification_finetuning/index.ipynb)
+    - [Advanced Tool Use](tool_use/index.ipynb)
+    - [Finetuning Agents](games/index.ipynb)
 
 - Tools, Development, and Deployment
-    - [Use MCP in DSPy](/tutorials/mcp/)
-    - [Output Refinement](/tutorials/output_refinement/best-of-n-and-refine/)
-    - [Saving and Loading](/tutorials/saving/)
-    - [Cache](/tutorials/cache/)
-    - [Deployment](/tutorials/deployment/)
-    - [Debugging & Observability](/tutorials/observability/)
-    - [Tracking DSPy Optimizers](/tutorials/optimizer_tracking/)
-    - [Streaming](/tutorials/streaming/)
-    - [Async](/tutorials/async/)
+    - [Use MCP in DSPy](mcp/index.md)
+    - [Output Refinement](output_refinement/best-of-n-and-refine.md)
+    - [Saving and Loading](saving/index.md)
+    - [Cache](cache/index.md)
+    - [Deployment](deployment/index.md)
+    - [Debugging & Observability](observability/index.md)
+    - [Tracking DSPy Optimizers](optimizer_tracking/index.md)
+    - [Streaming](streaming/index.md)
+    - [Async](async/index.md)
 
 
diff --git a/docs/docs/tutorials/observability/index.md b/docs/docs/tutorials/observability/index.md
index 1e338936..488bed25 100644
--- a/docs/docs/tutorials/observability/index.md
+++ b/docs/docs/tutorials/observability/index.md
@@ -190,7 +190,7 @@ the [MLflow Tracing Guide](https://mlflow.org/docs/3.0.0rc0/tracing).
 
 !!! info Learn more about MLflow
 
-    MLflow is an end-to-end LLMOps platform that offers extensive features like experiment tracking, evaluation, and deployment. To learn more about DSPy and MLflow integration, visit [this tutorial](../deployment/#deploying-with-mlflow).
+    MLflow is an end-to-end LLMOps platform that offers extensive features like experiment tracking, evaluation, and deployment. To learn more about DSPy and MLflow integration, visit [this tutorial](../deployment/index.md#deploying-with-mlflow).
 
 
 ## Building a Custom Logging Solution
diff --git a/docs/docs/tutorials/optimize_ai_program/index.md b/docs/docs/tutorials/optimize_ai_program/index.md
index 19c369e3..e8399a95 100644
--- a/docs/docs/tutorials/optimize_ai_program/index.md
+++ b/docs/docs/tutorials/optimize_ai_program/index.md
@@ -1,4 +1,21 @@
-- [Math Reasoning](/tutorials/math/)
-- [Classification Finetuning](/tutorials/classification_finetuning/)
-- [Advanced Tool Use](/tutorials/tool_use/)
-- [Finetuning Agents](/tutorials/games/)
\ No newline at end of file
+# Optimize AI Programs with DSPy
+
+This section focuses on DSPy's powerful optimization capabilities, demonstrating how to systematically improve your AI programs using various optimizers. These tutorials are lighter on programming concepts and instead showcase how DSPy optimizers can automatically enhance the quality and performance of your applications.
+
+## Mathematical and Reasoning Tasks
+
+### [Math Reasoning](../math/index.ipynb)
+Learn how to optimize DSPy programs for mathematical reasoning tasks. This tutorial demonstrates how optimizers can dramatically improve performance on complex math problems by finding better prompting strategies and few-shot examples.
+
+## Model Optimization
+
+### [Classification Finetuning](../classification_finetuning/index.ipynb)
+Discover how to use DSPy's finetuning optimizers to distill knowledge from large language models into smaller, more efficient models. Learn the complete workflow from prompt optimization to model finetuning for classification tasks.
+
+## Advanced Tool Integration
+
+### [Advanced Tool Use](../tool_use/index.ipynb)
+Explore how to optimize AI programs that use external tools and APIs. This tutorial shows how DSPy optimizers can learn to use tools more effectively, improving both accuracy and efficiency in tool-calling scenarios.
+
+### [Finetuning Agents](../games/index.ipynb)
+Learn to optimize complex agent-based systems through finetuning. This tutorial demonstrates how to improve agent performance in interactive environments like games, where strategic thinking and adaptation are crucial.
diff --git a/docs/docs/tutorials/rl_ai_program/index.md b/docs/docs/tutorials/rl_ai_program/index.md
index 7733f073..20ad0539 100644
--- a/docs/docs/tutorials/rl_ai_program/index.md
+++ b/docs/docs/tutorials/rl_ai_program/index.md
@@ -1,1 +1,11 @@
-See the links on the side bar.
\ No newline at end of file
+# Experimental RL Optimization for DSPy
+
+This section explores cutting-edge reinforcement learning (RL) approaches for optimizing DSPy programs. These experimental techniques represent the frontier of AI program optimization, combining the power of RL with DSPy's modular programming paradigm to achieve even better performance on complex tasks.
+
+## Advanced RL Optimization Techniques
+
+### [RL for Privacy-Conscious Delegation](../rl_papillon/index.ipynb)
+Explore how reinforcement learning can optimize privacy-conscious AI systems. This tutorial demonstrates how RL agents can learn to balance task performance with privacy constraints, making intelligent decisions about when and how to delegate sensitive operations.
+
+### [RL for Multi-Hop Research](../rl_multihop/index.ipynb)
+Learn to apply reinforcement learning to multi-hop reasoning tasks. This advanced tutorial shows how RL can optimize the search strategy in complex information retrieval scenarios, learning to navigate through multiple information sources more effectively.
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 1f77f071..364f9372 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -7,6 +7,7 @@ repo_name: stanfordnlp/dspy
 
 edit_uri: blob/main/docs/docs/
 docs_dir: "docs/"
+
 nav:
     - Get Started: index.md
     - Learn DSPy:
@@ -216,7 +217,7 @@ plugins:
                         show_warnings: false
                         html_meta: false
     - mkdocs-jupyter:
-        ignore_h1_titles: True
+        ignore_h1_titles: true
     - redirects:
         redirect_maps:
             # Redirect /intro/ to the main page
diff --git a/docs/scripts/generate_api_docs.py b/docs/scripts/generate_api_docs.py
index 10c33fe5..38e378e2 100644
--- a/docs/scripts/generate_api_docs.py
+++ b/docs/scripts/generate_api_docs.py
@@ -134,6 +134,7 @@ def generate_doc_page(name: str, module_path: str, obj: Any, is_root: bool = Fal
         members:
 {methods_list}"""
 
+    # We need to put ::: at last to avoid unclosed div. See https://github.com/danielfrg/mkdocs-jupyter/issues/231 for more details.
     return f"""<!-- START_API_REF -->
 ::: {module_path}.{name}
     handler: python
@@ -146,6 +147,7 @@ def generate_doc_page(name: str, module_path: str, obj: Any, is_root: bool = Fal
         show_object_full_path: false
         separate_signature: false
         inherited_members: true
+:::
 <!-- END_API_REF -->
 """
 
diff --git a/dspy/adapters/base.py b/dspy/adapters/base.py
index 1cdc1edf..3e0372c0 100644
--- a/dspy/adapters/base.py
+++ b/dspy/adapters/base.py
@@ -17,8 +17,9 @@ if TYPE_CHECKING:
 
 
 class Adapter:
-    def __init__(self, callbacks: list[BaseCallback] | None = None):
+    def __init__(self, callbacks: list[BaseCallback] | None = None, use_native_function_calling: bool = False):
         self.callbacks = callbacks or []
+        self.use_native_function_calling = use_native_function_calling
 
     def __init_subclass__(cls, **kwargs) -> None:
         super().__init_subclass__(**kwargs)
@@ -33,9 +34,8 @@ class Adapter:
         lm_kwargs: dict[str, Any],
         signature: Type[Signature],
         inputs: dict[str, Any],
-        use_native_function_calling: bool = False,
     ) -> dict[str, Any]:
-        if use_native_function_calling:
+        if self.use_native_function_calling:
             tool_call_input_field_name = self._get_tool_call_input_field_name(signature)
             tool_call_output_field_name = self._get_tool_call_output_field_name(signature)
 
@@ -57,6 +57,9 @@ class Adapter:
                 lm_kwargs["tools"] = litellm_tools
 
                 signature_for_native_function_calling = signature.delete(tool_call_output_field_name)
+                signature_for_native_function_calling = signature_for_native_function_calling.delete(
+                    tool_call_input_field_name
+                )
 
                 return signature_for_native_function_calling
 
@@ -64,12 +67,13 @@ class Adapter:
 
     def _call_postprocess(
         self,
-        signature: Type[Signature],
+        processed_signature: Type[Signature],
+        original_signature: Type[Signature],
         outputs: list[dict[str, Any]],
     ) -> list[dict[str, Any]]:
         values = []
 
-        tool_call_output_field_name = self._get_tool_call_output_field_name(signature)
+        tool_call_output_field_name = self._get_tool_call_output_field_name(original_signature)
 
         for output in outputs:
             output_logprobs = None
@@ -82,10 +86,14 @@ class Adapter:
                 tool_calls = output.get("tool_calls")
 
             if text:
-                value = self.parse(signature, text)
+                value = self.parse(processed_signature, text)
+                for field_name in original_signature.output_fields.keys():
+                    if field_name not in value:
+                        # We need to set the field not present in the processed signature to None for consistency.
+                        value[field_name] = None
             else:
                 value = {}
-                for field_name in signature.output_fields.keys():
+                for field_name in original_signature.output_fields.keys():
                     value[field_name] = None
 
             if tool_calls and tool_call_output_field_name:
@@ -117,7 +125,7 @@ class Adapter:
         inputs = self.format(processed_signature, demos, inputs)
 
         outputs = lm(messages=inputs, **lm_kwargs)
-        return self._call_postprocess(signature, outputs)
+        return self._call_postprocess(processed_signature, signature, outputs)
 
     async def acall(
         self,
@@ -131,7 +139,7 @@ class Adapter:
         inputs = self.format(processed_signature, demos, inputs)
 
         outputs = await lm.acall(messages=inputs, **lm_kwargs)
-        return self._call_postprocess(signature, outputs)
+        return self._call_postprocess(processed_signature, signature, outputs)
 
     def format(
         self,
diff --git a/dspy/adapters/chat_adapter.py b/dspy/adapters/chat_adapter.py
index ac407e47..4ace4214 100644
--- a/dspy/adapters/chat_adapter.py
+++ b/dspy/adapters/chat_adapter.py
@@ -15,7 +15,6 @@ from dspy.adapters.utils import (
 )
 from dspy.clients.lm import LM
 from dspy.signatures.signature import Signature
-from dspy.utils.callback import BaseCallback
 from dspy.utils.exceptions import AdapterParseError
 
 field_header_pattern = re.compile(r"\[\[ ## (\w+) ## \]\]")
@@ -27,9 +26,6 @@ class FieldInfoWithName(NamedTuple):
 
 
 class ChatAdapter(Adapter):
-    def __init__(self, callbacks: list[BaseCallback] | None = None):
-        super().__init__(callbacks)
-
     def __call__(
         self,
         lm: LM,
diff --git a/dspy/adapters/json_adapter.py b/dspy/adapters/json_adapter.py
index 9b14691c..51470e2e 100644
--- a/dspy/adapters/json_adapter.py
+++ b/dspy/adapters/json_adapter.py
@@ -9,6 +9,7 @@ import regex
 from pydantic.fields import FieldInfo
 
 from dspy.adapters.chat_adapter import ChatAdapter, FieldInfoWithName
+from dspy.adapters.types.tool import ToolCalls
 from dspy.adapters.utils import (
     format_field_value,
     get_annotation_name,
@@ -18,6 +19,7 @@ from dspy.adapters.utils import (
 )
 from dspy.clients.lm import LM
 from dspy.signatures.signature import Signature, SignatureMeta
+from dspy.utils.callback import BaseCallback
 from dspy.utils.exceptions import AdapterParseError
 
 logger = logging.getLogger(__name__)
@@ -37,6 +39,10 @@ def _has_open_ended_mapping(signature: SignatureMeta) -> bool:
 
 
 class JSONAdapter(ChatAdapter):
+    def __init__(self, callbacks: list[BaseCallback] | None = None, use_native_function_calling: bool = True):
+        # JSONAdapter uses native function calling by default.
+        super().__init__(callbacks=callbacks, use_native_function_calling=use_native_function_calling)
+
     def _json_adapter_call_common(self, lm, lm_kwargs, signature, demos, inputs, call_fn):
         """Common call logic to be used for both sync and async calls."""
         provider = lm.model.split("/", 1)[0] or "openai"
@@ -45,7 +51,10 @@ class JSONAdapter(ChatAdapter):
         if not params or "response_format" not in params:
             return call_fn(lm, lm_kwargs, signature, demos, inputs)
 
-        if _has_open_ended_mapping(signature):
+        has_tool_calls = any(field.annotation == ToolCalls for field in signature.output_fields.values())
+        if _has_open_ended_mapping(signature) or (not self.use_native_function_calling and has_tool_calls):
+            # We found that structured output mode doesn't work well with dspy.ToolCalls as output field.
+            # So we fall back to json mode if native function calling is disabled and ToolCalls is present.
             lm_kwargs["response_format"] = {"type": "json_object"}
             return call_fn(lm, lm_kwargs, signature, demos, inputs)
 
@@ -62,7 +71,9 @@ class JSONAdapter(ChatAdapter):
             return result
 
         try:
-            structured_output_model = _get_structured_outputs_response_format(signature)
+            structured_output_model = _get_structured_outputs_response_format(
+                signature, self.use_native_function_calling
+            )
             lm_kwargs["response_format"] = structured_output_model
             return super().__call__(lm, lm_kwargs, signature, demos, inputs)
         except Exception:
@@ -91,16 +102,6 @@ class JSONAdapter(ChatAdapter):
             lm_kwargs["response_format"] = {"type": "json_object"}
             return await super().acall(lm, lm_kwargs, signature, demos, inputs)
 
-    def _call_preprocess(
-        self,
-        lm: "LM",
-        lm_kwargs: dict[str, Any],
-        signature: Type[Signature],
-        inputs: dict[str, Any],
-        use_native_function_calling: bool = True,
-    ) -> dict[str, Any]:
-        return super()._call_preprocess(lm, lm_kwargs, signature, inputs, use_native_function_calling)
-
     def format_field_structure(self, signature: Type[Signature]) -> str:
         parts = []
         parts.append("All interactions will be structured in the following way, with the appropriate values filled in.")
@@ -206,7 +207,10 @@ class JSONAdapter(ChatAdapter):
         raise NotImplementedError
 
 
-def _get_structured_outputs_response_format(signature: SignatureMeta) -> type[pydantic.BaseModel]:
+def _get_structured_outputs_response_format(
+    signature: SignatureMeta,
+    use_native_function_calling: bool = True,
+) -> type[pydantic.BaseModel]:
     """
     Builds a Pydantic model from a DSPy signature's output_fields and ensures the generated JSON schema
     is compatible with OpenAI Structured Outputs (all objects have a "required" key listing every property,
@@ -227,6 +231,9 @@ def _get_structured_outputs_response_format(signature: SignatureMeta) -> type[py
     fields = {}
     for name, field in signature.output_fields.items():
         annotation = field.annotation
+        if use_native_function_calling and annotation == ToolCalls:
+            # Skip ToolCalls field if native function calling is enabled.
+            continue
         default = field.default if hasattr(field, "default") else ...
         fields[name] = (annotation, default)
 
diff --git a/dspy/adapters/two_step_adapter.py b/dspy/adapters/two_step_adapter.py
index 405cc99e..27b8d5e7 100644
--- a/dspy/adapters/two_step_adapter.py
+++ b/dspy/adapters/two_step_adapter.py
@@ -39,7 +39,8 @@ class TwoStepAdapter(Adapter):
     ```
     """
 
-    def __init__(self, extraction_model: LM):
+    def __init__(self, extraction_model: LM, **kwargs):
+        super().__init__(**kwargs)
         if not isinstance(extraction_model, LM):
             raise ValueError("extraction_model must be an instance of LM")
         self.extraction_model = extraction_model
diff --git a/dspy/primitives/base_module.py b/dspy/primitives/base_module.py
index d7005142..54bb024e 100644
--- a/dspy/primitives/base_module.py
+++ b/dspy/primitives/base_module.py
@@ -69,8 +69,8 @@ class BaseModule:
     def named_sub_modules(self, type_=None, skip_compiled=False) -> Generator[tuple[str, "BaseModule"], None, None]:
         """Find all sub-modules in the module, as well as their names.
 
-        Say self.children[4]['key'].sub_module is a sub-module. Then the name will be
-        'children[4][key].sub_module'. But if the sub-module is accessible at different
+        Say `self.children[4]['key'].sub_module` is a sub-module. Then the name will be
+        `children[4]['key'].sub_module`. But if the sub-module is accessible at different
         paths, only one of the paths will be returned.
         """
         if type_ is None:
diff --git a/dspy/primitives/module.py b/dspy/primitives/module.py
index e2928165..c95a6021 100644
--- a/dspy/primitives/module.py
+++ b/dspy/primitives/module.py
@@ -123,7 +123,7 @@ class Module(BaseModule, metaclass=ProgramMeta):
         return_failed_examples: bool = False,
         provide_traceback: bool | None = None,
         disable_progress_bar: bool = False,
-    ):
+    ) -> list[Example] | tuple[list[Example], list[Example], list[Exception]]:
         """
         Processes a list of dspy.Example instances in parallel using the Parallel module.
 
diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py
index 21f18f7a..1b92e60a 100644
--- a/dspy/signatures/signature.py
+++ b/dspy/signatures/signature.py
@@ -355,7 +355,7 @@ class Signature(BaseModel, metaclass=SignatureMeta):
         return signature_copy
 
 
-def ensure_signature(signature: str | Type[Signature], instructions=None) -> Signature:
+def ensure_signature(signature: str | Type[Signature], instructions=None) -> Type[Signature]:
     if signature is None:
         return None
     if isinstance(signature, str):
diff --git a/dspy/teleprompt/mipro_optimizer_v2.py b/dspy/teleprompt/mipro_optimizer_v2.py
index 4ab73b60..eb1a4d80 100644
--- a/dspy/teleprompt/mipro_optimizer_v2.py
+++ b/dspy/teleprompt/mipro_optimizer_v2.py
@@ -1,6 +1,5 @@
 import logging
 import random
-import select
 import sys
 import textwrap
 import time
@@ -395,9 +394,17 @@ class MIPROv2(Teleprompter):
         # Wait for input with timeout
         start_time = time.time()
         while time.time() - start_time < 20:
-            if select.select([sys.stdin], [], [], 0.1)[0]:
-                user_input = sys.stdin.readline().strip().lower()
-                return user_input == "y"
+            if sys.platform == "win32":
+                import msvcrt
+                if msvcrt.kbhit():
+                    user_input = msvcrt.getch().decode("utf-8").strip().lower()
+                    print(user_input)  # Echo the input
+                    return user_input == "y"
+            else:
+                import select
+                if select.select([sys.stdin], [], [], 0.1)[0]:
+                    user_input = sys.stdin.readline().strip().lower()
+                    return user_input == "y"
             time.sleep(0.1)
 
         print("\nNo input received within 20 seconds. Proceeding with execution...")


Test patch:
diff --git a/tests/adapters/test_chat_adapter.py b/tests/adapters/test_chat_adapter.py
index e599da58..457ba0b0 100644
--- a/tests/adapters/test_chat_adapter.py
+++ b/tests/adapters/test_chat_adapter.py
@@ -3,7 +3,7 @@ from unittest import mock
 
 import pydantic
 import pytest
-from litellm.utils import Choices, Message, ModelResponse
+from litellm.utils import ChatCompletionMessageToolCall, Choices, Function, Message, ModelResponse
 
 import dspy
 
@@ -422,3 +422,70 @@ async def test_chat_adapter_fallback_to_json_adapter_on_exception_async():
         # The parse should succeed
         result = await adapter.acall(lm, {}, signature, [], {"question": "What is the capital of France?"})
         assert result == [{"answer": "Paris"}]
+
+
+def test_chat_adapter_toolcalls_native_function_calling():
+    class MySignature(dspy.Signature):
+        question: str = dspy.InputField()
+        tools: list[dspy.Tool] = dspy.InputField()
+        answer: str = dspy.OutputField()
+        tool_calls: dspy.ToolCalls = dspy.OutputField()
+
+    def get_weather(city: str) -> str:
+        return f"The weather in {city} is sunny"
+
+    tools = [dspy.Tool(get_weather)]
+
+    adapter = dspy.JSONAdapter(use_native_function_calling=True)
+
+    # Case 1: Tool calls are present in the response, while content is None.
+    with mock.patch("litellm.completion") as mock_completion:
+        mock_completion.return_value = ModelResponse(
+            choices=[
+                Choices(
+                    finish_reason="tool_calls",
+                    index=0,
+                    message=Message(
+                        content=None,
+                        role="assistant",
+                        tool_calls=[
+                            ChatCompletionMessageToolCall(
+                                function=Function(arguments='{"city":"Paris"}', name="get_weather"),
+                                id="call_pQm8ajtSMxgA0nrzK2ivFmxG",
+                                type="function",
+                            )
+                        ],
+                    ),
+                ),
+            ],
+            model="openai/gpt-4o-mini",
+        )
+        result = adapter(
+            dspy.LM(model="openai/gpt-4o-mini", cache=False),
+            {},
+            MySignature,
+            [],
+            {"question": "What is the weather in Paris?", "tools": tools},
+        )
+
+        assert result[0]["tool_calls"] == dspy.ToolCalls(
+            tool_calls=[dspy.ToolCalls.ToolCall(name="get_weather", args={"city": "Paris"})]
+        )
+        # `answer` is not present, so we set it to None
+        assert result[0]["answer"] is None
+
+    # Case 2: Tool calls are not present in the response, while content is present.
+    with mock.patch("litellm.completion") as mock_completion:
+        mock_completion.return_value = ModelResponse(
+            choices=[Choices(message=Message(content="{'answer': 'Paris'}"))],
+            model="openai/gpt-4o-mini",
+        )
+        result = adapter(
+            dspy.LM(model="openai/gpt-4o-mini", cache=False),
+            {},
+            MySignature,
+            [],
+            {"question": "What is the weather in Paris?", "tools": tools},
+        )
+        assert result[0]["answer"] == "Paris"
+        assert result[0]["tool_calls"] is None
diff --git a/tests/adapters/test_json_adapter.py b/tests/adapters/test_json_adapter.py
index eb90a754..d58e5f9e 100644
--- a/tests/adapters/test_json_adapter.py
+++ b/tests/adapters/test_json_adapter.py
@@ -2,7 +2,7 @@ from unittest import mock
 
 import pydantic
 import pytest
-from litellm.utils import Choices, Message, ModelResponse
+from litellm.utils import ChatCompletionMessageToolCall, Choices, Function, Message, ModelResponse
 
 import dspy
 
@@ -650,3 +650,102 @@ async def test_error_message_on_json_adapter_failure_async():
                 await program.acall(question="Dummy question!")
 
             assert "ValueError!" in str(error.value)
+
+
+def test_json_adapter_toolcalls_native_function_calling():
+    class MySignature(dspy.Signature):
+        question: str = dspy.InputField()
+        tools: list[dspy.Tool] = dspy.InputField()
+        answer: str = dspy.OutputField()
+        tool_calls: dspy.ToolCalls = dspy.OutputField()
+
+    def get_weather(city: str) -> str:
+        return f"The weather in {city} is sunny"
+
+    tools = [dspy.Tool(get_weather)]
+
+    adapter = dspy.JSONAdapter(use_native_function_calling=True)
+
+    # Case 1: Tool calls are present in the response, while content is None.
+    with mock.patch("litellm.completion") as mock_completion:
+        mock_completion.return_value = ModelResponse(
+            choices=[
+                Choices(
+                    finish_reason="tool_calls",
+                    index=0,
+                    message=Message(
+                        content=None,
+                        role="assistant",
+                        tool_calls=[
+                            ChatCompletionMessageToolCall(
+                                function=Function(arguments='{"city":"Paris"}', name="get_weather"),
+                                id="call_pQm8ajtSMxgA0nrzK2ivFmxG",
+                                type="function",
+                            )
+                        ],
+                    ),
+                ),
+            ],
+            model="openai/gpt-4o-mini",
+        )
+        result = adapter(
+            dspy.LM(model="openai/gpt-4o-mini", cache=False),
+            {},
+            MySignature,
+            [],
+            {"question": "What is the weather in Paris?", "tools": tools},
+        )
+
+        assert result[0]["tool_calls"] == dspy.ToolCalls(
+            tool_calls=[dspy.ToolCalls.ToolCall(name="get_weather", args={"city": "Paris"})]
+        )
+        # `answer` is not present, so we set it to None
+        assert result[0]["answer"] is None
+
+    # Case 2: Tool calls are not present in the response, while content is present.
+    with mock.patch("litellm.completion") as mock_completion:
+        mock_completion.return_value = ModelResponse(
+            choices=[Choices(message=Message(content="{'answer': 'Paris'}"))],
+            model="openai/gpt-4o-mini",
+        )
+        result = adapter(
+            dspy.LM(model="openai/gpt-4o-mini", cache=False),
+            {},
+            MySignature,
+            [],
+            {"question": "What is the weather in Paris?", "tools": tools},
+        )
+        assert result[0]["answer"] == "Paris"
+        assert result[0]["tool_calls"] is None
+
+
+def test_json_adapter_toolcalls_no_native_function_calling():
+    class MySignature(dspy.Signature):
+        question: str = dspy.InputField()
+        tools: list[dspy.Tool] = dspy.InputField()
+        answer: str = dspy.OutputField()
+        tool_calls: dspy.ToolCalls = dspy.OutputField()
+
+    def get_weather(city: str) -> str:
+        return f"The weather in {city} is sunny"
+
+    tools = [dspy.Tool(get_weather)]
+
+    # Patch _get_structured_outputs_response_format to track calls
+    with mock.patch("dspy.adapters.json_adapter._get_structured_outputs_response_format") as mock_structured:
+        # Patch litellm.completion to return a dummy response
+        with mock.patch("litellm.completion") as mock_completion:
+            mock_completion.return_value = ModelResponse(
+                choices=[Choices(message=Message(content="{'answer': 'sunny', 'tool_calls': {'tool_calls': []}}"))],
+                model="openai/gpt-4o-mini",
+            )
+            adapter = dspy.JSONAdapter(use_native_function_calling=False)
+            lm = dspy.LM(model="openai/gpt-4o-mini", cache=False)
+            adapter(lm, {}, MySignature, [], {"question": "What is the weather in Tokyo?", "tools": tools})
+
+        # _get_structured_outputs_response_format is not called because without using native function calling,
+        # JSONAdapter falls back to json mode for stable quality.
+        mock_structured.assert_not_called()
+        mock_completion.assert_called_once()
+        _, call_kwargs = mock_completion.call_args
+        assert call_kwargs["response_format"] == {"type": "json_object"}
diff --git a/tests/adapters/test_tool.py b/tests/adapters/test_tool.py
index a4ab072d..20f59604 100644
--- a/tests/adapters/test_tool.py
+++ b/tests/adapters/test_tool.py
@@ -366,6 +366,7 @@ async def test_async_concurrent_calls():
     assert end_time - start_time < 0.3
 
 
+@pytest.mark.filterwarnings("ignore::RuntimeWarning")
 def test_async_tool_call_in_sync_mode():
     tool = Tool(async_dummy_function)
     with dspy.context(allow_tool_async_sync_conversion=False):
diff --git a/tests/clients/test_lm.py b/tests/clients/test_lm.py
index e1ff625d..b63406ba 100644
--- a/tests/clients/test_lm.py
+++ b/tests/clients/test_lm.py
@@ -301,7 +301,7 @@ def test_logprobs_included_when_requested():
         )
         result = lm("question")
         assert result[0]["text"] == "test answer"
-        assert result[0]["logprobs"].dict() == {
+        assert result[0]["logprobs"].model_dump() == {
             "content": [
                 {
                     "token": "test",
diff --git a/tests/signatures/test_signature.py b/tests/signatures/test_signature.py
index 9d58e1f7..2a18d5ac 100644
--- a/tests/signatures/test_signature.py
+++ b/tests/signatures/test_signature.py
@@ -1,3 +1,4 @@
+from types import UnionType
 from typing import Any, Dict, List, Optional, Tuple, Union
 
 import pydantic
@@ -436,3 +437,139 @@ def test_custom_type_from_different_module():
     path_obj = Path("/test/path")
     pred = dspy.Predict(test_signature)(input=path_obj)
     assert pred.output == "/test/path"
+
+def test_pep604_union_type_inline():
+    sig = Signature(
+        "input1: str | None, input2: None | int -> output_union: int | str"
+    )
+
+    # input1 and input2 test that both 'T | None' and 'None | T' are interpreted as Optional types,
+    # regardless of the order of None in the union expression.
+
+    assert "input1" in sig.input_fields
+    input1_annotation = sig.input_fields["input1"].annotation
+    assert input1_annotation == Optional[str] or (
+        getattr(input1_annotation, "__origin__", None) is Union
+        and str in input1_annotation.__args__
+        and type(None) in input1_annotation.__args__
+    )
+
+    assert "input2" in sig.input_fields
+    input2_annotation = sig.input_fields["input2"].annotation
+    assert input2_annotation == Optional[int] or (
+        getattr(input2_annotation, "__origin__", None) is Union
+        and int in input2_annotation.__args__
+        and type(None) in input2_annotation.__args__
+    )
+
+    assert "output_union" in sig.output_fields
+    output_union_annotation = sig.output_fields["output_union"].annotation
+    assert (
+        getattr(output_union_annotation, "__origin__", None) is Union
+        and int in output_union_annotation.__args__
+        and str in output_union_annotation.__args__
+    )
+
+
+def test_pep604_union_type_inline_equivalence():
+    sig1 = Signature("input: str | None -> output: int | str")
+    sig2 = Signature("input: Optional[str] -> output: Union[int, str]")
+
+    # PEP 604 union types in inline signatures should be equivalent to Optional and Union types
+    assert sig1.equals(sig2)
+
+    # Check that the annotations are equivalent
+    assert sig1.input_fields["input"].annotation == sig2.input_fields["input"].annotation
+    assert sig1.output_fields["output"].annotation == sig2.output_fields["output"].annotation
+
+
+def test_pep604_union_type_inline_nested():
+    sig = Signature(
+        "input: str | (int | float) | None -> output: str"
+    )
+    assert "input" in sig.input_fields
+    input_annotation = sig.input_fields["input"].annotation
+
+    # Check for the correct union: Union[str, int, float, NoneType]
+    assert getattr(input_annotation, "__origin__", None) is Union
+    assert set(input_annotation.__args__) == {str, int, float, type(None)}
+
+
+def test_pep604_union_type_class_nested():
+    class Sig1(Signature):
+        input: str | (int | float) | None = InputField()
+        output: str = OutputField()
+
+    assert "input" in Sig1.input_fields
+    input_annotation = Sig1.input_fields["input"].annotation
+
+    # Check for the correct union: UnionType[str, int, float, NoneType]
+    assert isinstance(input_annotation, UnionType)
+    assert set(input_annotation.__args__) == {str, int, float, type(None)}
+
+
+def test_pep604_union_type_class_equivalence():
+    class Sig1(Signature):
+        input: str | None = InputField()
+        output: int | str = OutputField()
+
+    class Sig2(Signature):
+        input: Optional[str] = InputField()  # noqa: UP045
+        output: Union[int, str] = OutputField()  # noqa: UP007
+
+    # PEP 604 union types in class signatures should be equivalent to Optional and Union types
+    assert Sig1.equals(Sig2)
+
+    # Check that the annotations are equivalent
+    assert Sig1.input_fields["input"].annotation == Sig2.input_fields["input"].annotation
+    assert Sig1.output_fields["output"].annotation == Sig2.output_fields["output"].annotation
+
+    # Check that the pep604 annotations are of type UnionType
+    assert isinstance(Sig1.input_fields["input"].annotation, UnionType)
+    assert isinstance(Sig1.output_fields["output"].annotation, UnionType)
+
+
+def test_pep604_union_type_insert():
+    class PEP604Signature(Signature):
+        input: str | None = InputField()
+        output: int | str = OutputField()
+
+    # This test ensures that inserting a field into a signature with a PEP 604 UnionType works
+
+    # Insert a new input field at the start
+    NewSig = PEP604Signature.prepend("new_input", InputField(), float | int)
+    assert "new_input" in NewSig.input_fields
+
+    new_input_annotation = NewSig.input_fields["new_input"].annotation
+    assert isinstance(new_input_annotation, UnionType)
+    assert set(new_input_annotation.__args__) == {float, int}
+
+    # The original union type field should still be present and correct
+    input_annotation = NewSig.input_fields["input"].annotation
+    output_annotation = NewSig.output_fields["output"].annotation
+
+    assert isinstance(input_annotation, UnionType)
+    assert str in input_annotation.__args__ and type(None) in input_annotation.__args__
+
+    assert isinstance(output_annotation, UnionType)
+    assert set(output_annotation.__args__) == {int, str}
+
+
+def test_pep604_union_type_with_custom_types():
+    class CustomType(pydantic.BaseModel):
+        value: str
+
+    sig = Signature(
+        "input: CustomType | None -> output: int | str",
+        custom_types={"CustomType": CustomType}
+    )
+
+    assert sig.input_fields["input"].annotation == Union[CustomType, None]
+    assert sig.output_fields["output"].annotation == Union[int, str]
+
+    lm = DummyLM([{"output": "processed"}])
+    dspy.settings.configure(lm=lm)
+
+    custom_obj = CustomType(value="test")
+    pred = dspy.Predict(sig)(input=custom_obj)
+    assert pred.output == "processed"