progress-observability 1.1.4__tar.gz → 1.2.0__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.
- {progress_observability-1.1.4 → progress_observability-1.2.0}/PKG-INFO +5 -4
- {progress_observability-1.1.4 → progress_observability-1.2.0}/README.md +4 -3
- progress_observability-1.2.0/build-python-instrumentation.sh +18 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/pyproject.toml +1 -1
- progress_observability-1.2.0/run-sample-azure.sh +8 -0
- progress_observability-1.2.0/run-sample-gemini.sh +8 -0
- progress_observability-1.2.0/run-sample-llamaindex-azure.sh +5 -0
- progress_observability-1.2.0/run-sample-llamaindex-gemini.sh +5 -0
- progress_observability-1.2.0/sample_apps/README.md +155 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/sample_apps/pyproject.toml +5 -1
- progress_observability-1.2.0/sample_apps/test_gemini_genAI.py +42 -0
- progress_observability-1.2.0/sample_apps/test_llamaindex_azure.py +57 -0
- progress_observability-1.2.0/sample_apps/test_llamaindex_gemini.py +54 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/helpers.py +54 -12
- progress_observability-1.2.0/src/progress/observability/model_fix_processor.py +823 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/sdk.py +43 -9
- progress_observability-1.2.0/update_sample_apps_wheel.sh +38 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/uv.lock +4 -4
- progress_observability-1.1.4/run-sample.sh +0 -37
- progress_observability-1.1.4/sample_apps/README.md +0 -13
- progress_observability-1.1.4/src/progress/observability/model_fix_processor.py +0 -804
- {progress_observability-1.1.4 → progress_observability-1.2.0}/.gitignore +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/.python-version +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/LICENSE +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/notices.txt +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/.gitignore +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/README.md +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/pyproject.toml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_multi_model_agents_ollama_dpc/instrument_auto-gemma2-2b-qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_multi_model_agents_openai_dpc/instrument_auto-gpt-4.1-nano-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_multi_model_agents_openai_dpc/instrument_auto-gpt-4o-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_multi_model_agents_openai_dpc/instrument_openai-gpt-4.1-nano-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_multi_model_agents_openai_dpc/instrument_openai-gpt-4o-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_bedrock_dpc/simple_agent_cs45-bedrock.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_ollama_dpc/simple_agent_gemma2-2b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_ollama_dpc/simple_agent_qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_openai_dpc/simple_agent_gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_openai_dpc/simple_agent_gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_simple_agents_openai_dpc/simple_agent_gpt-4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_two_provider_agent_anthropic_openai_dpc/instrument_auto-cs4-0514-gpt4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/cassettes/test_two_provider_agent_ollama_openai_dpc/instrument_auto-gpt-4o-mini-gemma2-2b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/conftest.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_multi_model_agents_ollama_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_multi_model_agents_openai_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_simple_agents_bedrock_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_simple_agents_ollama_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_simple_agents_openai_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_two_provider_agent_anthropic_openai_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/direct_provider_calls/tests/test_two_provider_agent_ollama_openai_dpc.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_multi_model_agents_ollama_fh/instrument_auto-gemma2-2b-qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_multi_model_agents_openai_fh/instrument_auto-gpt-4.1-nano-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_multi_model_agents_openai_fh/instrument_auto-gpt-4o-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_bedrock_fh/simple_agent_cs45-bedrock.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_ollama_fh/simple_agent_gemma2-2b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_ollama_fh/simple_agent_qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_openai_fh/simple_agent_gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_openai_fh/simple_agent_gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_simple_agents_openai_fh/simple_agent_gpt-4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_two_provider_agent_anthropic_openai_fh/instrument_auto-cs35-1022-gpt4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_two_provider_agent_ollama_openai_fh/instrument_auto-gemma2-2b-gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/cassettes/test_two_provider_agent_ollama_openai_fh/instrument_auto-qwen2.5-3b-gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/conftest.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_multi_model_agents_ollama_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_multi_model_agents_openai_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_simple_agents_bedrock_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_simple_agents_ollama_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_simple_agents_openai_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_two_provider_agent_anthropic_openai_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_haystack/tests/test_two_provider_agent_ollama_openai_fh.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_multi_model_agents_ollama_fl/instrument_auto-gemma2-2b-qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_multi_model_agents_openai_fl/instrument_auto-gpt-4.1-nano-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_multi_model_agents_openai_fl/instrument_auto-gpt-4o-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_anthropic_fl/simple_agent_claude-sonnet-4-20250514.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_bedrock_fl/simple_agent_cs45-bedrock.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_ollama_fl/simple_agent_gemma2-2b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_ollama_fl/simple_agent_qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_openai_fl/simple_agent_gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_openai_fl/simple_agent_gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_simple_agents_openai_fl/simple_agent_gpt-4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_two_provider_agent_anthropic_bedrock_fl/instrument_auto-cs4-0514-cs45-bedrock.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_two_provider_agent_anthropic_openai_fl/instrument_auto-cs4-0514-gpt4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_two_provider_agent_ollama_openai_fl/instrument_auto-gemma2-2b-gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/cassettes/test_two_provider_agent_ollama_openai_fl/instrument_auto-qwen2.5-3b-gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/conftest.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_multi_model_agents_ollama_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_multi_model_agents_openai_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_simple_agents_anthropic_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_simple_agents_bedrock_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_simple_agents_ollama_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_simple_agents_openai_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_two_provider_agent_anthropic_bedrock_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_two_provider_agent_anthropic_openai_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_langchain/tests/test_two_provider_agent_ollama_openai_fl.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_multi_model_agents_ollama_fllamaInd/instrument_auto-gemma2-2b-qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_multi_model_agents_openai_fllamaInd/instrument_auto-gpt-4.1-nano-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_multi_model_agents_openai_fllamaInd/instrument_auto-gpt-4o-gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agent_anthropic_fllamaInd/simple_agent_claude-3-5-haiku-20241022.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agent_anthropic_fllamaInd/simple_agent_claude-3-5-sonnet-20241022.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_bedrock_fllamaInd/simple_agent_cs45-bedrock.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_ollama_fllamaInd/simple_agent_gemma2-2b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_ollama_fllamaInd/simple_agent_qwen2.5-3b.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_openai_fllamaInd/simple_agent_gpt-4.1-nano.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_openai_fllamaInd/simple_agent_gpt-4o-mini.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_simple_agents_openai_fllamaInd/simple_agent_gpt-4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_two_provider_agent_anthropic_openai_fllamaInd/instrument_auto-cs35-1022-gpt4o.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_two_provider_agent_ollama_openai_fllamaInd/instrument_auto-gemma2-2b-gpt41n.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/cassettes/test_two_provider_agent_ollama_openai_fllamaInd/instrument_auto-qwen25-3b-gpt41n.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/conftest.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_multi_model_agents_ollama_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_multi_model_agents_openai_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_simple_agent_anthropic_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_simple_agents_bedrock_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_simple_agents_ollama_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_simple_agents_openai_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_two_provider_agent_anthropic_openai_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/framework_llama_index/tests/test_two_provider_agent_ollama_openai_fllamaInd.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/block_instruments.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/blocking_test.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/multiple_blocking.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/normal_test.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/partial_blocking.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/two_provider_block_ollama.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/cassettes/block_instruments/two_provider_block_openai.yaml +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/conftest.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/test_edge_cases.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/general_tests/test_isolated.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/test_utils/conftest_shared.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/src/test_utils/llm_span_helpers.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/progress.python.instrumentation.tests/uv.lock +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/sample_apps/.env.example +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/sample_apps/test_azure_langchain.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/sample_apps/uv.lock +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/__init__.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/constants.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/decorators.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/enums.py +0 -0
- {progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: progress-observability
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Progress Observability instrumentation for Python agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/telerik/agentclarity
|
|
6
6
|
Project-URL: Repository, https://github.com/telerik/agentclarity
|
|
@@ -49,9 +49,10 @@ Observability.instrument(
|
|
|
49
49
|
|
|
50
50
|
Environment overrides (optional):
|
|
51
51
|
|
|
52
|
-
- `OBSERVABILITY_APP_NAME`
|
|
53
|
-
- `OBSERVABILITY_ENDPOINT`
|
|
54
|
-
- `OBSERVABILITY_API_KEY`
|
|
52
|
+
- `OBSERVABILITY_APP_NAME` sets the application name (e.g. "my-app")
|
|
53
|
+
- `OBSERVABILITY_ENDPOINT` sets a custom endpoint for collecting data (e.g. "https://collector.observability.progress.com:443")
|
|
54
|
+
- `OBSERVABILITY_API_KEY` sets the authentication key to use when sending data
|
|
55
|
+
- `OBSERVABILITY_TRACE_CONTENT` when set to "false" will stop sending LLM prompt/responses as part of the telemetry data
|
|
55
56
|
|
|
56
57
|
Auth headers are added automatically for HTTP(S) endpoints when api_key is provided.
|
|
57
58
|
|
|
@@ -31,9 +31,10 @@ Observability.instrument(
|
|
|
31
31
|
|
|
32
32
|
Environment overrides (optional):
|
|
33
33
|
|
|
34
|
-
- `OBSERVABILITY_APP_NAME`
|
|
35
|
-
- `OBSERVABILITY_ENDPOINT`
|
|
36
|
-
- `OBSERVABILITY_API_KEY`
|
|
34
|
+
- `OBSERVABILITY_APP_NAME` sets the application name (e.g. "my-app")
|
|
35
|
+
- `OBSERVABILITY_ENDPOINT` sets a custom endpoint for collecting data (e.g. "https://collector.observability.progress.com:443")
|
|
36
|
+
- `OBSERVABILITY_API_KEY` sets the authentication key to use when sending data
|
|
37
|
+
- `OBSERVABILITY_TRACE_CONTENT` when set to "false" will stop sending LLM prompt/responses as part of the telemetry data
|
|
37
38
|
|
|
38
39
|
Auth headers are added automatically for HTTP(S) endpoints when api_key is provided.
|
|
39
40
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e # Exit on any error
|
|
4
|
+
|
|
5
|
+
echo "🔨 Building Progress Observability Python SDK..."
|
|
6
|
+
uv build
|
|
7
|
+
|
|
8
|
+
echo "📦 Copying wheel to sample_apps..."
|
|
9
|
+
cp dist/progress_observability-*.whl sample_apps/
|
|
10
|
+
|
|
11
|
+
# 🔁 Update sample_apps/pyproject.toml to point to the latest wheel
|
|
12
|
+
bash ./update_sample_apps_wheel.sh
|
|
13
|
+
|
|
14
|
+
echo "📥 Installing dependencies in sample_apps..."
|
|
15
|
+
cd sample_apps
|
|
16
|
+
uv sync
|
|
17
|
+
|
|
18
|
+
echo "✅ Done!"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
## Python sample apps
|
|
2
|
+
|
|
3
|
+
This directory contains example Python applications that demonstrate how to use the Progress Observability SDK with:
|
|
4
|
+
|
|
5
|
+
- Azure OpenAI + LangChain (`test_azure_langchain.py`)
|
|
6
|
+
- Google GenAI Gemini (`test_gemini_genAI.py`)
|
|
7
|
+
- Azure OpenAI + LlamaIndex (`test_llamaindex_azure.py`)
|
|
8
|
+
- Google GenAI Gemini + LlamaIndex (`test_llamaindex_gemini.py`)
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- Python (use the version defined in `pyproject.toml`)
|
|
13
|
+
- [uv](https://github.com/astral-sh/uv) installed locally
|
|
14
|
+
- Access to:
|
|
15
|
+
- Azure OpenAI (for the Azure example)
|
|
16
|
+
- Google GenAI (for the Gemini example)
|
|
17
|
+
|
|
18
|
+
## 1. Build the SDK and prepare the sample apps
|
|
19
|
+
|
|
20
|
+
From the repo root:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd Progress.Observability.Instrumentation
|
|
24
|
+
bash build-python-instrumentation.sh
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This will:
|
|
28
|
+
|
|
29
|
+
- build the Progress Observability Python SDK (`uv build`)
|
|
30
|
+
- copy the latest `.whl` file into `sample_apps/`
|
|
31
|
+
- update `sample_apps/pyproject.toml` to point to the latest wheel
|
|
32
|
+
- install dependencies in `sample_apps` (`uv sync`)
|
|
33
|
+
|
|
34
|
+
After this step you can run the samples from the `sample_apps` directory.
|
|
35
|
+
|
|
36
|
+
## 2. Configure environment variables (`.env`)
|
|
37
|
+
|
|
38
|
+
In the `sample_apps/` directory:
|
|
39
|
+
|
|
40
|
+
1. Use `.env.example` as a template (if present) or create a `.env` file manually.
|
|
41
|
+
2. Set the following variables as needed.
|
|
42
|
+
|
|
43
|
+
Minimum for the Azure LangChain sample:
|
|
44
|
+
|
|
45
|
+
```env
|
|
46
|
+
AZURE_API_KEY=...
|
|
47
|
+
AZURE_API_ENDPOINT=...
|
|
48
|
+
AZURE_API_VERSION=...
|
|
49
|
+
OBSERVABILITY_ENDPOINT=...
|
|
50
|
+
OBSERVABILITY_API_KEY=...
|
|
51
|
+
APP_ID=local-azure
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Minimum for the Gemini sample:
|
|
55
|
+
|
|
56
|
+
```env
|
|
57
|
+
GEMINI_API_KEY=...
|
|
58
|
+
OBSERVABILITY_ENDPOINT=...
|
|
59
|
+
OBSERVABILITY_API_KEY=...
|
|
60
|
+
APP_ID=local-gemini
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You can reuse the same `OBSERVABILITY_*` values for both samples.
|
|
64
|
+
|
|
65
|
+
Minimum for the Azure LlamaIndex sample (same Azure OpenAI settings as LangChain):
|
|
66
|
+
|
|
67
|
+
```env
|
|
68
|
+
AZURE_API_KEY=...
|
|
69
|
+
AZURE_API_ENDPOINT=...
|
|
70
|
+
AZURE_API_VERSION=...
|
|
71
|
+
OBSERVABILITY_ENDPOINT=...
|
|
72
|
+
OBSERVABILITY_API_KEY=...
|
|
73
|
+
APP_ID=local-llamaindex-azure
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Minimum for the Gemini LlamaIndex sample (same Gemini settings as the basic sample):
|
|
77
|
+
|
|
78
|
+
```env
|
|
79
|
+
GEMINI_API_KEY=...
|
|
80
|
+
OBSERVABILITY_ENDPOINT=...
|
|
81
|
+
OBSERVABILITY_API_KEY=...
|
|
82
|
+
APP_ID=local-llamaindex-gemini
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can reuse the same `OBSERVABILITY_*` values and provider keys across all samples; only `APP_ID` needs to differ if you want to see separate applications in the UI.
|
|
86
|
+
|
|
87
|
+
## 3. Run only the Azure LangChain sample
|
|
88
|
+
|
|
89
|
+
From the repo root:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
cd Progress.Observability.Instrumentation
|
|
93
|
+
bash run-sample-azure.sh
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This will:
|
|
97
|
+
|
|
98
|
+
- change into `sample_apps/`
|
|
99
|
+
- run `uv run python test_azure_langchain.py`
|
|
100
|
+
|
|
101
|
+
## 4. Run only the Gemini (Google GenAI) sample
|
|
102
|
+
|
|
103
|
+
From the repo root:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cd Progress.Observability.Instrumentation
|
|
107
|
+
bash run-sample-gemini.sh
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This will:
|
|
111
|
+
|
|
112
|
+
- change into `sample_apps/`
|
|
113
|
+
- run `uv run python test_gemini_genAI.py`
|
|
114
|
+
|
|
115
|
+
## 5. Run only the Azure LlamaIndex sample
|
|
116
|
+
|
|
117
|
+
From the repo root:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cd Progress.Observability.Instrumentation
|
|
121
|
+
bash run-sample-llamaindex-azure.sh
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This will:
|
|
125
|
+
|
|
126
|
+
- change into `sample_apps/`
|
|
127
|
+
- run `uv run python test_llamaindex_azure.py`
|
|
128
|
+
|
|
129
|
+
## 6. Run only the Gemini LlamaIndex sample
|
|
130
|
+
|
|
131
|
+
From the repo root:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
cd Progress.Observability.Instrumentation
|
|
135
|
+
bash run-sample-llamaindex-gemini.sh
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This will:
|
|
139
|
+
|
|
140
|
+
- change into `sample_apps/`
|
|
141
|
+
- run `uv run python test_llamaindex_gemini.py`
|
|
142
|
+
|
|
143
|
+
## 7. Run scripts directly from `sample_apps` (optional)
|
|
144
|
+
|
|
145
|
+
If you have already run `bash build-python-instrumentation.sh` at least once and dependencies are installed, you can run the scripts directly:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cd Progress.Observability.Instrumentation/sample_apps
|
|
149
|
+
|
|
150
|
+
# Azure LangChain
|
|
151
|
+
uv run python test_azure_langchain.py
|
|
152
|
+
|
|
153
|
+
# Gemini
|
|
154
|
+
uv run python test_gemini_genAI.py
|
|
155
|
+
```
|
|
@@ -6,9 +6,13 @@ readme = "README.md"
|
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"dotenv>=0.9.9",
|
|
9
|
+
"google-genai>=1.56.0",
|
|
9
10
|
"langchain-openai>=1.0.2",
|
|
10
11
|
"progress-observability",
|
|
12
|
+
"llama-index-core>=0.12.0",
|
|
13
|
+
"llama-index-llms-azure-openai>=0.2.0",
|
|
14
|
+
"llama-index-llms-google-genai>=0.2.0",
|
|
11
15
|
]
|
|
12
16
|
|
|
13
17
|
[tool.uv.sources]
|
|
14
|
-
progress-observability = { path = "progress_observability-
|
|
18
|
+
progress-observability = { path = "progress_observability-0.0.0-py3-none-any.whl" }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
from progress.observability import Observability
|
|
4
|
+
from google import genai
|
|
5
|
+
|
|
6
|
+
load_dotenv()
|
|
7
|
+
|
|
8
|
+
def instrument_observability(app_name: str | None = None) -> None:
|
|
9
|
+
api_key = os.environ.get("OBSERVABILITY_API_KEY")
|
|
10
|
+
endpoint = os.environ.get("OBSERVABILITY_ENDPOINT")
|
|
11
|
+
app_id = os.environ.get("APP_ID", "default")
|
|
12
|
+
|
|
13
|
+
Observability.instrument(
|
|
14
|
+
app_name=app_name or f"ai-gemini-python-example-{app_id}",
|
|
15
|
+
endpoint=endpoint,
|
|
16
|
+
api_key=api_key,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def build_gemini_client():
|
|
20
|
+
api_key = os.environ.get("GEMINI_API_KEY")
|
|
21
|
+
return genai.Client(api_key=api_key)
|
|
22
|
+
|
|
23
|
+
def run_gemini_demo(question: str = "What is the capital of France?") -> str:
|
|
24
|
+
|
|
25
|
+
instrument_observability()
|
|
26
|
+
client = build_gemini_client()
|
|
27
|
+
|
|
28
|
+
response = client.models.generate_content(
|
|
29
|
+
model="gemini-2.5-flash",
|
|
30
|
+
contents=question,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
text = getattr(response, "text", None) or str(response)
|
|
34
|
+
print(f"Question: {question}")
|
|
35
|
+
print(f"Answer: {text}")
|
|
36
|
+
return text
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
# Allow running this file directly as a tiny sample app
|
|
40
|
+
run_gemini_demo()
|
|
41
|
+
|
|
42
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
from progress.observability import Observability
|
|
6
|
+
from llama_index.llms.azure_openai import AzureOpenAI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
QUESTION = "What is the capital of Portugal?"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def instrument_observability() -> None:
|
|
13
|
+
load_dotenv()
|
|
14
|
+
|
|
15
|
+
endpoint = os.getenv("OBSERVABILITY_ENDPOINT")
|
|
16
|
+
api_key = os.getenv("OBSERVABILITY_API_KEY")
|
|
17
|
+
app_id = os.getenv("APP_ID", "local")
|
|
18
|
+
|
|
19
|
+
Observability.instrument(
|
|
20
|
+
app_name=f"llamaindex-azure-python-example-{app_id}",
|
|
21
|
+
endpoint=endpoint,
|
|
22
|
+
api_key=api_key,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_llm() -> AzureOpenAI:
|
|
27
|
+
# Prefer AZURE_OPENAI_* variables if provided, otherwise fall back to AZURE_API_*
|
|
28
|
+
api_key = os.getenv("AZURE_OPENAI_API_KEY") or os.getenv("AZURE_API_KEY")
|
|
29
|
+
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT") or os.getenv("AZURE_API_ENDPOINT")
|
|
30
|
+
api_version = os.getenv("AZURE_OPENAI_API_VERSION") or os.getenv("AZURE_API_VERSION")
|
|
31
|
+
deployment = os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME") or "gpt-4o-mini"
|
|
32
|
+
|
|
33
|
+
return AzureOpenAI(
|
|
34
|
+
engine=deployment,
|
|
35
|
+
model=deployment,
|
|
36
|
+
azure_endpoint=endpoint,
|
|
37
|
+
api_key=api_key,
|
|
38
|
+
api_version=api_version,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_llamaindex_azure_demo(question: str = QUESTION) -> str:
|
|
43
|
+
instrument_observability()
|
|
44
|
+
|
|
45
|
+
llm = build_llm()
|
|
46
|
+
|
|
47
|
+
print(f"Question: {question}")
|
|
48
|
+
|
|
49
|
+
response = llm.complete(question)
|
|
50
|
+
|
|
51
|
+
answer = str(response)
|
|
52
|
+
print(f"Answer: {answer}")
|
|
53
|
+
return answer
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
run_llamaindex_azure_demo()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
from progress.observability import Observability
|
|
6
|
+
from llama_index.llms.google_genai import GoogleGenAI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
QUESTION = "What is the capital of Greece?"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def instrument_observability() -> None:
|
|
13
|
+
load_dotenv()
|
|
14
|
+
|
|
15
|
+
endpoint = os.getenv("OBSERVABILITY_ENDPOINT")
|
|
16
|
+
api_key = os.getenv("OBSERVABILITY_API_KEY")
|
|
17
|
+
app_id = os.getenv("APP_ID", "local")
|
|
18
|
+
|
|
19
|
+
Observability.instrument(
|
|
20
|
+
app_name=f"llamaindex-gemini-python-example-{app_id}",
|
|
21
|
+
endpoint=endpoint,
|
|
22
|
+
api_key=api_key,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_llm() -> GoogleGenAI:
|
|
27
|
+
api_key = os.getenv("GEMINI_API_KEY")
|
|
28
|
+
|
|
29
|
+
if not api_key:
|
|
30
|
+
raise RuntimeError("GEMINI_API_KEY must be set")
|
|
31
|
+
|
|
32
|
+
return GoogleGenAI(
|
|
33
|
+
model="gemini-2.5-flash",
|
|
34
|
+
api_key=api_key,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def run_llamaindex_gemini_demo(question: str = QUESTION) -> str:
|
|
39
|
+
instrument_observability()
|
|
40
|
+
|
|
41
|
+
llm = build_llm()
|
|
42
|
+
|
|
43
|
+
print(f"Question: {question}")
|
|
44
|
+
|
|
45
|
+
# Direct LLM call via the LlamaIndex GoogleGenAI adapter, no VectorStoreIndex/embeddings
|
|
46
|
+
response = llm.complete(question)
|
|
47
|
+
|
|
48
|
+
answer = str(response)
|
|
49
|
+
print(f"Answer: {answer}")
|
|
50
|
+
return answer
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
run_llamaindex_gemini_demo()
|
{progress_observability-1.1.4 → progress_observability-1.2.0}/src/progress/observability/helpers.py
RENAMED
|
@@ -6,6 +6,8 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
from typing import Optional, Dict, Any
|
|
8
8
|
from .constants import SDK_ENV_VARS
|
|
9
|
+
import logging
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
9
11
|
|
|
10
12
|
class ObservabilityTelemetry:
|
|
11
13
|
"""Helper class for Progress Observability"""
|
|
@@ -89,10 +91,9 @@ def observability_get_default_span_processor(
|
|
|
89
91
|
return get_default_span_processor(api_key, disable_batch, api_endpoint, headers, exporter)
|
|
90
92
|
|
|
91
93
|
|
|
92
|
-
def init_environment(app_name: str,
|
|
94
|
+
def init_environment(app_name: str, trace_content: Optional[bool]) -> str:
|
|
93
95
|
"""Initialize environment variables with Observability overrides"""
|
|
94
|
-
|
|
95
|
-
api_key = os.getenv("OBSERVABILITY_API_KEY") or api_key
|
|
96
|
+
# Endpoint and api_key resolution and validation is handled before this function is called
|
|
96
97
|
app_name = os.getenv("OBSERVABILITY_APP_NAME") or app_name
|
|
97
98
|
|
|
98
99
|
# Handle trace_content from environment variable or parameter
|
|
@@ -117,17 +118,13 @@ def init_environment(app_name: str, endpoint: Optional[str], api_key: Optional[s
|
|
|
117
118
|
# Suppress metrics exporter 404 errors (underlying SDK doesn't fully respect the env var)
|
|
118
119
|
logger = logging.getLogger("opentelemetry.exporter.otlp.proto.http.metric_exporter")
|
|
119
120
|
logger.disabled = True
|
|
120
|
-
|
|
121
|
-
# Validate endpoint format if provided
|
|
122
|
-
if endpoint and not (endpoint.startswith("http://") or endpoint.startswith("https://") or ":" in endpoint):
|
|
123
|
-
raise ValueError(f"Invalid endpoint format: {endpoint}")
|
|
124
121
|
|
|
125
|
-
return app_name
|
|
122
|
+
return app_name
|
|
123
|
+
|
|
124
|
+
|
|
126
125
|
def setup_api_key_headers(api_key: str, endpoint: Optional[str], init_kwargs: Dict[str, Any], kwargs: Dict[str, Any]) -> None:
|
|
127
|
-
"""Setup
|
|
128
|
-
#
|
|
129
|
-
if not isinstance(api_key, str) or not api_key.strip():
|
|
130
|
-
raise ValueError("API key must be a non-empty string")
|
|
126
|
+
"""Setup authentication headers for HTTP endpoints"""
|
|
127
|
+
# API key validation is handled by Observability._validate_api_key() before this function is called
|
|
131
128
|
|
|
132
129
|
# Only add headers for HTTP endpoints
|
|
133
130
|
if is_http_endpoint(endpoint):
|
|
@@ -141,3 +138,48 @@ def setup_api_key_headers(api_key: str, endpoint: Optional[str], init_kwargs: Di
|
|
|
141
138
|
"X-Api-Key": api_key
|
|
142
139
|
}
|
|
143
140
|
init_kwargs["headers"] = headers
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def safe_extract_nested(data: Any, *keys: str, default: Any = None, debug: bool = False) -> Any:
|
|
144
|
+
"""
|
|
145
|
+
Safely extract a nested dictionary value using a sequence of keys.
|
|
146
|
+
|
|
147
|
+
This helper provides defensive access to nested dictionary structures,
|
|
148
|
+
commonly found in JSON responses from AI providers. It handles:
|
|
149
|
+
- Missing keys gracefully
|
|
150
|
+
- Non-dict intermediate values
|
|
151
|
+
- Empty dict results
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
data: The root dictionary or data structure to extract from
|
|
155
|
+
*keys: Variable number of keys to traverse (e.g., 'raw', 'usage', 'inputTokens')
|
|
156
|
+
default: Value to return if extraction fails or result is empty. Defaults to None.
|
|
157
|
+
debug: If True, emit debug logs using the module logger.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
The extracted value if found, otherwise the default value.
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
>>> data = {"raw": {"usage": {"inputTokens": 100}}}
|
|
164
|
+
>>> safe_extract_nested(data, 'raw', 'usage', 'inputTokens')
|
|
165
|
+
100
|
|
166
|
+
>>> safe_extract_nested(data, 'raw', 'missing', 'key', default=0)
|
|
167
|
+
0
|
|
168
|
+
"""
|
|
169
|
+
result = data
|
|
170
|
+
for key in keys:
|
|
171
|
+
if not isinstance(result, dict):
|
|
172
|
+
if debug:
|
|
173
|
+
logger.debug(f"safe_extract_nested: Expected dict at key '{key}', got {type(result).__name__}")
|
|
174
|
+
return default
|
|
175
|
+
result = result.get(key)
|
|
176
|
+
if result is None:
|
|
177
|
+
if debug:
|
|
178
|
+
logger.debug(f"safe_extract_nested: Key '{key}' not found in path {keys}")
|
|
179
|
+
return default
|
|
180
|
+
|
|
181
|
+
# Return default if result is empty dict
|
|
182
|
+
if result == {}:
|
|
183
|
+
return default
|
|
184
|
+
|
|
185
|
+
return result
|