deepeval 3.4.7__py3-none-any.whl → 3.4.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. deepeval/__init__.py +8 -7
  2. deepeval/_version.py +1 -1
  3. deepeval/cli/dotenv_handler.py +71 -0
  4. deepeval/cli/main.py +1021 -280
  5. deepeval/cli/utils.py +116 -2
  6. deepeval/confident/api.py +29 -14
  7. deepeval/config/__init__.py +0 -0
  8. deepeval/config/settings.py +565 -0
  9. deepeval/config/settings_manager.py +133 -0
  10. deepeval/config/utils.py +86 -0
  11. deepeval/dataset/__init__.py +1 -0
  12. deepeval/dataset/dataset.py +70 -10
  13. deepeval/dataset/test_run_tracer.py +82 -0
  14. deepeval/dataset/utils.py +23 -0
  15. deepeval/key_handler.py +64 -2
  16. deepeval/metrics/__init__.py +4 -1
  17. deepeval/metrics/answer_relevancy/template.py +7 -2
  18. deepeval/metrics/conversational_dag/__init__.py +7 -0
  19. deepeval/metrics/conversational_dag/conversational_dag.py +139 -0
  20. deepeval/metrics/conversational_dag/nodes.py +931 -0
  21. deepeval/metrics/conversational_dag/templates.py +117 -0
  22. deepeval/metrics/dag/dag.py +13 -4
  23. deepeval/metrics/dag/graph.py +47 -15
  24. deepeval/metrics/dag/utils.py +103 -38
  25. deepeval/metrics/faithfulness/template.py +11 -8
  26. deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/template.py +6 -4
  27. deepeval/metrics/multimodal_metrics/multimodal_faithfulness/template.py +6 -4
  28. deepeval/metrics/tool_correctness/tool_correctness.py +7 -3
  29. deepeval/models/llms/amazon_bedrock_model.py +24 -3
  30. deepeval/models/llms/openai_model.py +37 -41
  31. deepeval/models/retry_policy.py +280 -0
  32. deepeval/openai_agents/agent.py +4 -2
  33. deepeval/synthesizer/chunking/doc_chunker.py +87 -51
  34. deepeval/test_run/api.py +1 -0
  35. deepeval/tracing/otel/exporter.py +20 -8
  36. deepeval/tracing/otel/utils.py +57 -0
  37. deepeval/tracing/tracing.py +37 -16
  38. deepeval/tracing/utils.py +98 -1
  39. deepeval/utils.py +111 -70
  40. {deepeval-3.4.7.dist-info → deepeval-3.4.9.dist-info}/METADATA +3 -1
  41. {deepeval-3.4.7.dist-info → deepeval-3.4.9.dist-info}/RECORD +44 -34
  42. deepeval/env.py +0 -35
  43. {deepeval-3.4.7.dist-info → deepeval-3.4.9.dist-info}/LICENSE.md +0 -0
  44. {deepeval-3.4.7.dist-info → deepeval-3.4.9.dist-info}/WHEEL +0 -0
  45. {deepeval-3.4.7.dist-info → deepeval-3.4.9.dist-info}/entry_points.txt +0 -0
deepeval/cli/utils.py CHANGED
@@ -1,7 +1,13 @@
1
- from rich import print
1
+ from __future__ import annotations
2
+
3
+ import os
2
4
  import webbrowser
3
5
  import pyfiglet
4
- from typing import Optional
6
+
7
+ from enum import Enum
8
+ from pathlib import Path
9
+ from rich import print
10
+ from typing import Optional, Dict, Iterable, List, Tuple, Union
5
11
  from opentelemetry.trace import Span
6
12
 
7
13
  from deepeval.key_handler import (
@@ -14,8 +20,25 @@ from deepeval.test_run.test_run import (
14
20
  global_test_run_manager,
15
21
  )
16
22
  from deepeval.confident.api import get_confident_api_key, set_confident_api_key
23
+ from deepeval.cli.dotenv_handler import DotenvHandler
17
24
 
25
+
26
+ StrOrEnum = Union[str, "Enum"]
18
27
  PROD = "https://app.confident-ai.com"
28
+ # List all mutually exclusive USE_* keys
29
+ USE_MODEL_KEYS: List[ModelKeyValues | EmbeddingKeyValues] = [
30
+ ModelKeyValues.USE_OPENAI_MODEL,
31
+ ModelKeyValues.USE_AZURE_OPENAI,
32
+ ModelKeyValues.USE_LOCAL_MODEL,
33
+ ModelKeyValues.USE_GROK_MODEL,
34
+ ModelKeyValues.USE_MOONSHOT_MODEL,
35
+ ModelKeyValues.USE_DEEPSEEK_MODEL,
36
+ ModelKeyValues.USE_GEMINI_MODEL,
37
+ ModelKeyValues.USE_LITELLM,
38
+ EmbeddingKeyValues.USE_AZURE_OPENAI_EMBEDDING,
39
+ EmbeddingKeyValues.USE_LOCAL_EMBEDDINGS,
40
+ # MAINTENANCE: add more if new USE_* keys appear
41
+ ]
19
42
 
20
43
 
21
44
  def render_login_message():
@@ -65,3 +88,94 @@ def clear_evaluation_model_keys():
65
88
  def clear_embedding_model_keys():
66
89
  for key in EmbeddingKeyValues:
67
90
  KEY_FILE_HANDLER.remove_key(key)
91
+
92
+
93
+ def _to_str_key(k: StrOrEnum) -> str:
94
+ return k.value if hasattr(k, "value") else str(k)
95
+
96
+
97
+ def _normalize_kv(updates: Dict[StrOrEnum, str]) -> Dict[str, str]:
98
+ return {_to_str_key(k): v for k, v in updates.items()}
99
+
100
+
101
+ def _normalize_keys(keys: Iterable[StrOrEnum]) -> list[str]:
102
+ return [_to_str_key(k) for k in keys]
103
+
104
+
105
+ def _parse_save_option(
106
+ save_opt: str | None, default_path: str = ".env.local"
107
+ ) -> Tuple[bool, str | None]:
108
+ if not save_opt:
109
+ return False, None
110
+ kind, *rest = save_opt.split(":", 1)
111
+ if kind != "dotenv":
112
+ return False, None
113
+ path = rest[0] if rest else default_path
114
+ return True, path
115
+
116
+
117
+ def resolve_save_target(save_opt: Optional[str]) -> Optional[str]:
118
+ """
119
+ Returns a normalized save target string like 'dotenv:.env.local' or None.
120
+ Precedence:
121
+ 1) --save=...
122
+ 2) DEEPEVAL_DEFAULT_SAVE (opt-in project default)
123
+ 3) None (no save)
124
+ """
125
+ if save_opt:
126
+ return save_opt
127
+
128
+ env_default = os.getenv("DEEPEVAL_DEFAULT_SAVE")
129
+ if env_default and env_default.strip():
130
+ return env_default.strip()
131
+
132
+ return None
133
+
134
+
135
+ def save_environ_to_store(
136
+ save_opt: str | None, updates: Dict[StrOrEnum, str]
137
+ ) -> Tuple[bool, str | None]:
138
+ """
139
+ Save 'updates' into the selected store (currently only dotenv). Idempotent upsert.
140
+ Returns (handled, path).
141
+ """
142
+ ok, path = _parse_save_option(save_opt)
143
+ if not ok:
144
+ return False, None
145
+ if updates:
146
+ DotenvHandler(path).upsert(_normalize_kv(updates))
147
+ return True, path
148
+
149
+
150
+ def unset_environ_in_store(
151
+ save_opt: str | None, keys: Iterable[StrOrEnum]
152
+ ) -> Tuple[bool, str | None]:
153
+ """
154
+ Remove keys from the selected store (currently only dotenv).
155
+ Returns (handled, path).
156
+ """
157
+ ok, path = _parse_save_option(save_opt)
158
+ if not ok:
159
+ return False, None
160
+ norm = _normalize_keys(keys)
161
+ if norm:
162
+ DotenvHandler(path).unset(norm)
163
+ return True, path
164
+
165
+
166
+ def switch_model_provider(target: ModelKeyValues, save: str = None) -> None:
167
+ """
168
+ Ensure exactly one USE_* model flag is set to "YES" and the rest to "NO",
169
+ both in the .deepeval json store and in a dotenv file (if save is provided).
170
+ """
171
+ if target not in USE_MODEL_KEYS:
172
+ raise ValueError(f"{target} is not a recognized USE_* model key")
173
+
174
+ for key in USE_MODEL_KEYS:
175
+ value = "YES" if key == target else "NO"
176
+ KEY_FILE_HANDLER.write_key(key, value)
177
+
178
+ if save:
179
+ handled, path = save_environ_to_store(save, {key: value})
180
+ if not handled:
181
+ print("Unsupported --save option. Use --save=dotenv[:path].")
deepeval/confident/api.py CHANGED
@@ -14,6 +14,8 @@ from tenacity import (
14
14
  import deepeval
15
15
  from deepeval.key_handler import KEY_FILE_HANDLER, KeyValues
16
16
  from deepeval.confident.types import ApiResponse, ConfidentApiError
17
+ from deepeval.config.settings import get_settings
18
+
17
19
 
18
20
  CONFIDENT_API_KEY_ENV_VAR = "CONFIDENT_API_KEY"
19
21
  DEEPEVAL_BASE_URL = "https://deepeval.confident-ai.com"
@@ -31,20 +33,33 @@ def get_base_api_url():
31
33
  return API_BASE_URL
32
34
 
33
35
 
34
- def get_confident_api_key():
35
- return os.getenv(CONFIDENT_API_KEY_ENV_VAR) or KEY_FILE_HANDLER.fetch_data(
36
- KeyValues.API_KEY
37
- )
38
-
39
-
40
- def set_confident_api_key(api_key: Union[str, None]):
41
- if api_key is None:
42
- KEY_FILE_HANDLER.remove_key(KeyValues.API_KEY)
43
- os.environ.pop(CONFIDENT_API_KEY_ENV_VAR, None)
44
- return
45
-
46
- KEY_FILE_HANDLER.write_key(KeyValues.API_KEY, api_key)
47
- os.environ[CONFIDENT_API_KEY_ENV_VAR] = api_key
36
+ def get_confident_api_key() -> Optional[str]:
37
+ s = get_settings()
38
+ key: Optional[SecretStr] = s.CONFIDENT_API_KEY or s.API_KEY
39
+ return key.get_secret_value() if key else None
40
+
41
+
42
+ def set_confident_api_key(api_key: Optional[str]) -> None:
43
+ """
44
+ - Always updates runtime (os.environ) via settings.edit()
45
+ - If DEEPEVAL_DEFAULT_SAVE is set, also persists to dotenv
46
+ - Never writes secrets to the legacy JSON keystore (your Settings logic already skips secrets)
47
+ """
48
+ s = get_settings()
49
+ save = (
50
+ s.DEEPEVAL_DEFAULT_SAVE or None
51
+ ) # e.g. "dotenv" or "dotenv:/path/.env"
52
+
53
+ # If you *only* want runtime changes unless a default save is present:
54
+ if save is None:
55
+ with s.edit(persist=False):
56
+ s.CONFIDENT_API_KEY = SecretStr(api_key) if api_key else None
57
+ s.API_KEY = SecretStr(api_key) if api_key else None
58
+ else:
59
+ # Respect default save: update runtime + write to dotenv, but not JSON
60
+ with s.edit(save=save, persist=None):
61
+ s.CONFIDENT_API_KEY = SecretStr(api_key) if api_key else None
62
+ s.API_KEY = SecretStr(api_key) if api_key else None
48
63
 
49
64
 
50
65
  def is_confident():
File without changes