edsl 0.1.47__py3-none-any.whl → 0.1.49__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 (314) hide show
  1. edsl/__init__.py +44 -39
  2. edsl/__version__.py +1 -1
  3. edsl/agents/__init__.py +4 -2
  4. edsl/agents/{Agent.py → agent.py} +442 -152
  5. edsl/agents/{AgentList.py → agent_list.py} +220 -162
  6. edsl/agents/descriptors.py +46 -7
  7. edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
  8. edsl/base/__init__.py +75 -0
  9. edsl/base/base_class.py +1303 -0
  10. edsl/base/data_transfer_models.py +114 -0
  11. edsl/base/enums.py +215 -0
  12. edsl/base.py +8 -0
  13. edsl/buckets/__init__.py +25 -0
  14. edsl/buckets/bucket_collection.py +324 -0
  15. edsl/buckets/model_buckets.py +206 -0
  16. edsl/buckets/token_bucket.py +502 -0
  17. edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
  18. edsl/buckets/token_bucket_client.py +509 -0
  19. edsl/caching/__init__.py +20 -0
  20. edsl/caching/cache.py +814 -0
  21. edsl/caching/cache_entry.py +427 -0
  22. edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
  23. edsl/caching/exceptions.py +24 -0
  24. edsl/caching/orm.py +30 -0
  25. edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
  26. edsl/caching/sql_dict.py +441 -0
  27. edsl/config/__init__.py +8 -0
  28. edsl/config/config_class.py +177 -0
  29. edsl/config.py +4 -176
  30. edsl/conversation/Conversation.py +7 -7
  31. edsl/conversation/car_buying.py +4 -4
  32. edsl/conversation/chips.py +6 -6
  33. edsl/coop/__init__.py +25 -2
  34. edsl/coop/coop.py +311 -75
  35. edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
  36. edsl/coop/exceptions.py +62 -0
  37. edsl/coop/price_fetcher.py +126 -0
  38. edsl/coop/utils.py +89 -24
  39. edsl/data_transfer_models.py +5 -72
  40. edsl/dataset/__init__.py +10 -0
  41. edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
  42. edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
  43. edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
  44. edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
  45. edsl/{results → dataset/display}/table_renderers.py +58 -2
  46. edsl/{results → dataset}/file_exports.py +4 -5
  47. edsl/{results → dataset}/smart_objects.py +2 -2
  48. edsl/enums.py +5 -205
  49. edsl/inference_services/__init__.py +5 -0
  50. edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
  51. edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
  52. edsl/inference_services/data_structures.py +3 -2
  53. edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
  54. edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
  55. edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
  56. edsl/inference_services/registry.py +4 -41
  57. edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
  58. edsl/inference_services/services/__init__.py +31 -0
  59. edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
  60. edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
  61. edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
  62. edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
  63. edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
  64. edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
  65. edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
  66. edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
  67. edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
  68. edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
  69. edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
  70. edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
  71. edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
  72. edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
  73. edsl/inference_services/write_available.py +1 -2
  74. edsl/instructions/__init__.py +6 -0
  75. edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
  76. edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
  77. edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
  78. edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
  79. edsl/interviews/__init__.py +4 -0
  80. edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
  81. edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
  82. edsl/interviews/interview.py +638 -0
  83. edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
  84. edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
  85. edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
  86. edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
  87. edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
  88. edsl/invigilators/__init__.py +38 -0
  89. edsl/invigilators/invigilator_base.py +477 -0
  90. edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
  91. edsl/invigilators/prompt_constructor.py +476 -0
  92. edsl/{agents → invigilators}/prompt_helpers.py +2 -1
  93. edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
  94. edsl/{agents → invigilators}/question_option_processor.py +96 -21
  95. edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
  96. edsl/jobs/__init__.py +7 -1
  97. edsl/jobs/async_interview_runner.py +99 -35
  98. edsl/jobs/check_survey_scenario_compatibility.py +7 -5
  99. edsl/jobs/data_structures.py +153 -22
  100. edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
  101. edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
  102. edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
  103. edsl/jobs/{Jobs.py → jobs.py} +313 -167
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
  106. edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
  107. edsl/jobs/jobs_pricing_estimation.py +347 -0
  108. edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
  109. edsl/jobs/jobs_runner_asyncio.py +282 -0
  110. edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
  111. edsl/jobs/results_exceptions_handler.py +2 -2
  112. edsl/key_management/__init__.py +28 -0
  113. edsl/key_management/key_lookup.py +161 -0
  114. edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
  115. edsl/key_management/key_lookup_collection.py +82 -0
  116. edsl/key_management/models.py +218 -0
  117. edsl/language_models/__init__.py +7 -2
  118. edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
  119. edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
  120. edsl/language_models/language_model.py +1080 -0
  121. edsl/language_models/model.py +10 -25
  122. edsl/language_models/{ModelList.py → model_list.py} +9 -14
  123. edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
  124. edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
  125. edsl/language_models/repair.py +4 -4
  126. edsl/language_models/utilities.py +4 -4
  127. edsl/notebooks/__init__.py +3 -1
  128. edsl/notebooks/{Notebook.py → notebook.py} +7 -8
  129. edsl/prompts/__init__.py +1 -1
  130. edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
  131. edsl/prompts/{Prompt.py → prompt.py} +101 -95
  132. edsl/questions/HTMLQuestion.py +1 -1
  133. edsl/questions/__init__.py +154 -25
  134. edsl/questions/answer_validator_mixin.py +1 -1
  135. edsl/questions/compose_questions.py +4 -3
  136. edsl/questions/derived/question_likert_five.py +166 -0
  137. edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
  138. edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
  139. edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
  140. edsl/questions/descriptors.py +24 -30
  141. edsl/questions/loop_processor.py +65 -19
  142. edsl/questions/question_base.py +881 -0
  143. edsl/questions/question_base_gen_mixin.py +15 -16
  144. edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
  145. edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
  146. edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
  147. edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
  148. edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
  149. edsl/questions/question_free_text.py +282 -0
  150. edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
  151. edsl/questions/{QuestionList.py → question_list.py} +6 -7
  152. edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
  153. edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
  154. edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
  155. edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
  156. edsl/questions/question_registry.py +4 -9
  157. edsl/questions/register_questions_meta.py +8 -4
  158. edsl/questions/response_validator_abc.py +17 -16
  159. edsl/results/__init__.py +4 -1
  160. edsl/{exceptions/results.py → results/exceptions.py} +1 -1
  161. edsl/results/report.py +197 -0
  162. edsl/results/{Result.py → result.py} +131 -45
  163. edsl/results/{Results.py → results.py} +365 -220
  164. edsl/results/results_selector.py +344 -25
  165. edsl/scenarios/__init__.py +30 -3
  166. edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
  167. edsl/scenarios/directory_scanner.py +156 -13
  168. edsl/scenarios/document_chunker.py +186 -0
  169. edsl/scenarios/exceptions.py +101 -0
  170. edsl/scenarios/file_methods.py +2 -3
  171. edsl/scenarios/{FileStore.py → file_store.py} +275 -189
  172. edsl/scenarios/handlers/__init__.py +14 -14
  173. edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
  174. edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
  175. edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
  176. edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
  177. edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
  178. edsl/scenarios/handlers/latex_file_store.py +5 -0
  179. edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
  180. edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
  181. edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
  182. edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
  183. edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
  184. edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
  185. edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
  186. edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
  187. edsl/scenarios/scenario.py +928 -0
  188. edsl/scenarios/scenario_join.py +18 -5
  189. edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
  190. edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
  191. edsl/scenarios/scenario_selector.py +5 -1
  192. edsl/study/ObjectEntry.py +2 -2
  193. edsl/study/SnapShot.py +5 -5
  194. edsl/study/Study.py +18 -19
  195. edsl/study/__init__.py +6 -4
  196. edsl/surveys/__init__.py +7 -4
  197. edsl/surveys/dag/__init__.py +2 -0
  198. edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
  199. edsl/surveys/{DAG.py → dag/dag.py} +13 -10
  200. edsl/surveys/descriptors.py +1 -1
  201. edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
  202. edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
  203. edsl/surveys/memory/__init__.py +3 -0
  204. edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
  205. edsl/surveys/rules/__init__.py +3 -0
  206. edsl/surveys/{Rule.py → rules/rule.py} +103 -43
  207. edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
  208. edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
  209. edsl/surveys/survey.py +1743 -0
  210. edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
  211. edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
  212. edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
  213. edsl/tasks/__init__.py +32 -0
  214. edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
  215. edsl/tasks/task_creators.py +135 -0
  216. edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
  217. edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
  218. edsl/tasks/task_status_log.py +85 -0
  219. edsl/tokens/__init__.py +2 -0
  220. edsl/tokens/interview_token_usage.py +53 -0
  221. edsl/utilities/PrettyList.py +1 -1
  222. edsl/utilities/SystemInfo.py +25 -22
  223. edsl/utilities/__init__.py +29 -21
  224. edsl/utilities/gcp_bucket/__init__.py +2 -0
  225. edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
  226. edsl/utilities/interface.py +44 -536
  227. edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
  228. edsl/utilities/repair_functions.py +1 -1
  229. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/METADATA +1 -1
  230. edsl-0.1.49.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -493
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/coop/PriceFetcher.py +0 -54
  236. edsl/data/Cache.py +0 -582
  237. edsl/data/CacheEntry.py +0 -238
  238. edsl/data/SQLiteDict.py +0 -292
  239. edsl/data/__init__.py +0 -5
  240. edsl/data/orm.py +0 -10
  241. edsl/exceptions/cache.py +0 -5
  242. edsl/exceptions/coop.py +0 -14
  243. edsl/exceptions/data.py +0 -14
  244. edsl/exceptions/scenarios.py +0 -29
  245. edsl/jobs/Answers.py +0 -43
  246. edsl/jobs/JobsPrompts.py +0 -354
  247. edsl/jobs/buckets/BucketCollection.py +0 -134
  248. edsl/jobs/buckets/ModelBuckets.py +0 -65
  249. edsl/jobs/buckets/TokenBucket.py +0 -283
  250. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  251. edsl/jobs/interviews/Interview.py +0 -395
  252. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  253. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  254. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  255. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  256. edsl/jobs/tasks/TaskCreators.py +0 -64
  257. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  258. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  259. edsl/language_models/LanguageModel.py +0 -635
  260. edsl/language_models/ServiceDataSources.py +0 -0
  261. edsl/language_models/key_management/KeyLookup.py +0 -63
  262. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  263. edsl/language_models/key_management/models.py +0 -137
  264. edsl/questions/QuestionBase.py +0 -544
  265. edsl/questions/QuestionFreeText.py +0 -130
  266. edsl/questions/derived/QuestionLikertFive.py +0 -76
  267. edsl/results/ResultsExportMixin.py +0 -45
  268. edsl/results/TextEditor.py +0 -50
  269. edsl/results/results_fetch_mixin.py +0 -33
  270. edsl/results/results_tools_mixin.py +0 -98
  271. edsl/scenarios/DocumentChunker.py +0 -104
  272. edsl/scenarios/Scenario.py +0 -548
  273. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  274. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  275. edsl/scenarios/handlers/latex.py +0 -5
  276. edsl/shared.py +0 -1
  277. edsl/surveys/Survey.py +0 -1301
  278. edsl/surveys/SurveyQualtricsImport.py +0 -284
  279. edsl/surveys/SurveyToApp.py +0 -141
  280. edsl/surveys/instructions/__init__.py +0 -0
  281. edsl/tools/__init__.py +0 -1
  282. edsl/tools/clusters.py +0 -192
  283. edsl/tools/embeddings.py +0 -27
  284. edsl/tools/embeddings_plotting.py +0 -118
  285. edsl/tools/plotting.py +0 -112
  286. edsl/tools/summarize.py +0 -18
  287. edsl/utilities/data/Registry.py +0 -6
  288. edsl/utilities/data/__init__.py +0 -1
  289. edsl/utilities/data/scooter_results.json +0 -1
  290. edsl-0.1.47.dist-info/RECORD +0 -354
  291. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  292. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  293. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  294. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  295. /edsl/{results → dataset/display}/table_display.css +0 -0
  296. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  297. /edsl/{results → dataset}/tree_explore.py +0 -0
  298. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  299. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  300. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  301. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  302. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  303. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  304. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  305. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  306. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  307. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  308. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  309. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  310. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  311. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  312. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  313. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/WHEEL +0 -0
@@ -2,19 +2,16 @@ import textwrap
2
2
  from random import random
3
3
  from typing import Optional, TYPE_CHECKING, List, Callable
4
4
 
5
- from edsl.utilities.PrettyList import PrettyList
6
- from edsl.config import CONFIG
5
+ from ..utilities import PrettyList
6
+ from ..config import CONFIG
7
7
 
8
- from edsl.inference_services.InferenceServicesCollection import (
9
- InferenceServicesCollection,
10
- )
11
- from edsl.inference_services.data_structures import AvailableModels
12
- from edsl.inference_services.InferenceServiceABC import InferenceServiceABC
13
- from edsl.enums import InferenceServiceLiteral
14
- from edsl.exceptions.inference_services import InferenceServiceError
8
+ from ..inference_services import (InferenceServicesCollection,
9
+ AvailableModels, InferenceServiceABC, InferenceServiceError, default)
10
+
11
+ from ..enums import InferenceServiceLiteral
15
12
 
16
13
  if TYPE_CHECKING:
17
- from edsl.results.Dataset import Dataset
14
+ from ..dataset import Dataset
18
15
 
19
16
 
20
17
  def get_model_class(
@@ -22,8 +19,6 @@ def get_model_class(
22
19
  registry: Optional[InferenceServicesCollection] = None,
23
20
  service_name: Optional[InferenceServiceLiteral] = None,
24
21
  ):
25
- from edsl.inference_services.registry import default
26
-
27
22
  registry = registry or default
28
23
  try:
29
24
  factory = registry.create_model_factory(model_name, service_name=service_name)
@@ -31,7 +26,6 @@ def get_model_class(
31
26
  except (InferenceServiceError, Exception) as e:
32
27
  return Model._handle_model_error(model_name, e)
33
28
 
34
-
35
29
  class Meta(type):
36
30
  def __repr__(cls):
37
31
  return textwrap.dedent(
@@ -59,8 +53,6 @@ class Model(metaclass=Meta):
59
53
  def get_registry(cls) -> InferenceServicesCollection:
60
54
  """Get the current registry or initialize with default if None"""
61
55
  if cls._registry is None:
62
- from edsl.inference_services.registry import default
63
-
64
56
  cls._registry = default
65
57
  return cls._registry
66
58
 
@@ -165,10 +157,8 @@ class Model(metaclass=Meta):
165
157
  @classmethod
166
158
  def key_info(cls, obscure_api_key: bool = True) -> "Dataset":
167
159
  """Returns a dataset of local key information."""
168
- from edsl.language_models.key_management.KeyLookupCollection import (
169
- KeyLookupCollection,
170
- )
171
- from edsl.scenarios import Scenario, ScenarioList
160
+ from ..key_management import KeyLookupCollection
161
+ from ..scenarios import Scenario, ScenarioList
172
162
 
173
163
  klc = KeyLookupCollection()
174
164
  klc.add_key_lookup(fetch_order=None)
@@ -290,7 +280,7 @@ class Model(metaclass=Meta):
290
280
  works_with_text: Optional[bool] = None,
291
281
  works_with_images: Optional[bool] = None,
292
282
  ) -> list[dict]:
293
- from edsl.coop import Coop
283
+ from ..coop import Coop
294
284
 
295
285
  c = Coop()
296
286
  working_models = c.fetch_working_models()
@@ -349,10 +339,5 @@ class Model(metaclass=Meta):
349
339
 
350
340
  if __name__ == "__main__":
351
341
  import doctest
352
-
353
342
  doctest.testmod(optionflags=doctest.ELLIPSIS)
354
343
 
355
- # available = Model.available()
356
- # m = Model("gpt-4-1106-preview")
357
- # results = m.execute_model_call("Hello world")
358
- # print(results)
@@ -1,17 +1,14 @@
1
1
  from typing import Optional, List, TYPE_CHECKING
2
2
  from collections import UserList
3
3
 
4
- from edsl.Base import Base
5
- from edsl.language_models.model import Model
4
+ from ..base import Base
5
+ from ..language_models import Model
6
6
 
7
- #
8
- from edsl.utilities.remove_edsl_version import remove_edsl_version
9
- from edsl.utilities.is_valid_variable_name import is_valid_variable_name
7
+ from ..utilities import remove_edsl_version, is_valid_variable_name, dict_hash
10
8
 
11
9
  if TYPE_CHECKING:
12
- from edsl.inference_services.data_structures import AvailableModels
13
- from edsl.language_models import LanguageModel
14
-
10
+ from ..inference_services.data_structures import AvailableModels
11
+ from ..language_models import LanguageModel
15
12
 
16
13
  class ModelList(Base, UserList):
17
14
  __documentation__ = """https://docs.expectedparrot.com/en/latest/language_models.html#module-edsl.language_models.ModelList"""
@@ -50,13 +47,11 @@ class ModelList(Base, UserList):
50
47
  True
51
48
 
52
49
  """
53
- from edsl.utilities.utilities import dict_hash
54
-
55
50
  return dict_hash(self.to_dict(sort=True, add_edsl_version=False))
56
51
 
57
52
  def to_scenario_list(self):
58
- from edsl.scenarios.ScenarioList import ScenarioList
59
- from edsl.scenarios.Scenario import Scenario
53
+ from ..scenarios import ScenarioList
54
+ from ..scenarios import Scenario
60
55
 
61
56
  sl = ScenarioList()
62
57
  for model in self:
@@ -107,7 +102,7 @@ class ModelList(Base, UserList):
107
102
  ]
108
103
  }
109
104
  if add_edsl_version:
110
- from edsl import __version__
105
+ from .. import __version__
111
106
 
112
107
  d["edsl_version"] = __version__
113
108
  d["edsl_class_name"] = "ModelList"
@@ -140,7 +135,7 @@ class ModelList(Base, UserList):
140
135
  >>> newm = ModelList.from_dict(ModelList.example().to_dict())
141
136
  >>> assert ModelList.example() == newm
142
137
  """
143
- from edsl.language_models.LanguageModel import LanguageModel
138
+ from ..language_models import LanguageModel
144
139
 
145
140
  return cls(data=[LanguageModel.from_dict(model) for model in data["models"]])
146
141
 
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from typing import Optional, Any, List
3
- from edsl.exceptions.language_models import LanguageModelBadResponseError
3
+ from .exceptions import LanguageModelBadResponseError
4
4
 
5
5
  from json_repair import repair_json
6
6
 
@@ -2,7 +2,7 @@ from abc import ABC, ABCMeta
2
2
  from typing import Any, List, Callable
3
3
  import inspect
4
4
  from typing import get_type_hints
5
- from edsl.exceptions.language_models import LanguageModelAttributeTypeError
5
+ from .exceptions import LanguageModelAttributeTypeError
6
6
  from edsl.enums import InferenceServiceType
7
7
 
8
8
 
@@ -6,7 +6,7 @@ import warnings
6
6
  async def async_repair(
7
7
  bad_json, error_message="", user_prompt=None, system_prompt=None, cache=None
8
8
  ):
9
- from edsl.utilities.utilities import clean_json
9
+ from ..utilities import clean_json
10
10
 
11
11
  s = clean_json(bad_json)
12
12
 
@@ -22,7 +22,7 @@ async def async_repair(
22
22
  return valid_dict, success
23
23
 
24
24
  try:
25
- from edsl.utilities.repair_functions import extract_json_from_string
25
+ from ..utilities.repair_functions import extract_json_from_string
26
26
 
27
27
  valid_dict = extract_json_from_string(s)
28
28
  success = True
@@ -32,11 +32,11 @@ async def async_repair(
32
32
  else:
33
33
  return valid_dict, success
34
34
 
35
- from edsl.language_models.model import Model
35
+ from .model import Model
36
36
 
37
37
  m = Model()
38
38
 
39
- from edsl.questions.QuestionExtract import QuestionExtract
39
+ from ..questions.QuestionExtract import QuestionExtract
40
40
 
41
41
  with warnings.catch_warnings():
42
42
  warnings.simplefilter("ignore", UserWarning)
@@ -1,11 +1,12 @@
1
1
  import asyncio
2
2
  from typing import Any, Optional, List
3
- from edsl.enums import InferenceServiceType
3
+ from ..enums import InferenceServiceType
4
+ from ..surveys import Survey
4
5
 
6
+ from .language_model import LanguageModel
5
7
 
6
8
  def create_survey(num_questions: int, chained: bool = True, take_scenario=False):
7
- from edsl.surveys.Survey import Survey
8
- from edsl.questions.QuestionFreeText import QuestionFreeText
9
+ from ..questions import QuestionFreeText
9
10
 
10
11
  survey = Survey()
11
12
  for i in range(num_questions):
@@ -27,7 +28,6 @@ def create_survey(num_questions: int, chained: bool = True, take_scenario=False)
27
28
  def create_language_model(
28
29
  exception: Exception, fail_at_number: int, never_ending=False
29
30
  ):
30
- from edsl.language_models.LanguageModel import LanguageModel
31
31
 
32
32
  class LanguageModelFromUtilities(LanguageModel):
33
33
  _model_ = "test"
@@ -1 +1,3 @@
1
- from edsl.notebooks.Notebook import Notebook
1
+ from .notebook import Notebook
2
+
3
+ __all__ = ["Notebook"]
@@ -4,9 +4,9 @@ from __future__ import annotations
4
4
  import json
5
5
  from typing import Dict, List, Optional, Union
6
6
  from uuid import uuid4
7
- from edsl.Base import Base
8
- from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
9
7
 
8
+ from ..base import Base
9
+ from ..utilities.decorators import remove_edsl_version
10
10
 
11
11
  class Notebook(Base):
12
12
  """
@@ -102,7 +102,7 @@ class Notebook(Base):
102
102
  """
103
103
  Allow the model to be used as a key in a dictionary.
104
104
  """
105
- from edsl.utilities.utilities import dict_hash
105
+ from ..utilities.utilities import dict_hash
106
106
 
107
107
  return dict_hash(self.data["cells"])
108
108
 
@@ -112,7 +112,7 @@ class Notebook(Base):
112
112
  """
113
113
  d = {"name": self.name, "data": self.data}
114
114
  if add_edsl_version:
115
- from edsl import __version__
115
+ from .. import __version__
116
116
 
117
117
  d["edsl_version"] = __version__
118
118
  d["edsl_class_name"] = self.__class__.__name__
@@ -241,7 +241,7 @@ class Notebook(Base):
241
241
  Return the code that could be used to create this Notebook.
242
242
  """
243
243
  lines = []
244
- lines.append("from edsl import Notebook")
244
+ lines.append("from edsl import Notebook") # Keep as absolute for code generation
245
245
  lines.append(f'nb = Notebook(data={self.data}, name="""{self.name}""")')
246
246
  return lines
247
247
 
@@ -251,13 +251,12 @@ class Notebook(Base):
251
251
 
252
252
  :param filename: Name of the output folder and main tex file (without extension)
253
253
  """
254
- from edsl.notebooks.NotebookToLaTeX import NotebookToLaTeX
254
+ from .NotebookToLaTeX import NotebookToLaTeX
255
255
 
256
256
  NotebookToLaTeX(self).convert(filename)
257
257
 
258
258
 
259
259
  if __name__ == "__main__":
260
- from edsl import Notebook
261
-
260
+ from .. import Notebook
262
261
  notebook = Notebook.example()
263
262
  assert notebook == notebook.from_dict(notebook.to_dict())
edsl/prompts/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # from edsl.prompts.registry import get_classes
2
- from edsl.prompts.Prompt import Prompt
2
+ from .prompt import Prompt
@@ -1,4 +1,6 @@
1
- class PromptError(Exception):
1
+ from ..base import BaseException
2
+
3
+ class PromptError(BaseException):
2
4
  pass
3
5
 
4
6
 
@@ -1,33 +1,50 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, List, Union, Dict, Optional
3
3
  from pathlib import Path
4
+ import time
5
+ from functools import lru_cache
4
6
 
5
- # from jinja2 import Undefined
6
-
7
+ from jinja2 import Environment, meta, Undefined
7
8
 
8
- from edsl.exceptions.prompts import TemplateRenderError
9
- from edsl.Base import PersistenceMixin, RepresentationMixin
9
+ from .exceptions import TemplateRenderError
10
+ from ..base import PersistenceMixin, RepresentationMixin
10
11
 
11
12
  MAX_NESTING = 100
12
13
 
13
- from jinja2 import Environment, meta, TemplateSyntaxError, Undefined
14
- from functools import lru_cache
15
-
16
14
  class PreserveUndefined(Undefined):
17
15
  def __str__(self):
18
16
  return "{{ " + str(self._undefined_name) + " }}"
19
17
 
20
- # Create environment once at module level
21
- _env = Environment(undefined=PreserveUndefined)
18
+ class TemplateVars:
19
+ """Stores variables set during template rendering."""
20
+ def __init__(self):
21
+ self.data = {}
22
+
23
+ def set(self, name, value):
24
+ """Store a variable with its name and value,
25
+ returning an empty string for direct use in the template.
26
+ """
27
+ self.data[name] = value
28
+ return ""
29
+
30
+ def get(self, name, default=None):
31
+ """Retrieve a stored variable."""
32
+ return self.data.get(name, default)
33
+
34
+ def get_all(self) -> Dict[str, Any]:
35
+ """Return all captured variables."""
36
+ return self.data
22
37
 
23
- @lru_cache(maxsize=1024)
24
- def _compile_template(text: str):
25
- return _env.from_string(text)
38
+ def make_env() -> Environment:
39
+ """Create a fresh Jinja environment each time,
40
+ so we don't mix captured variables across multiple renders.
41
+ """
42
+ return Environment(undefined=PreserveUndefined)
26
43
 
27
44
  @lru_cache(maxsize=1024)
28
- def _find_template_variables(template: str) -> list[str]:
29
- """Find and return the template variables."""
30
- ast = _env.parse(template)
45
+ def _find_template_variables(template_text: str) -> List[str]:
46
+ env = make_env()
47
+ ast = env.parse(template_text)
31
48
  return list(meta.find_undeclared_variables(ast))
32
49
 
33
50
  def _make_hashable(value):
@@ -81,9 +98,10 @@ class Prompt(PersistenceMixin, RepresentationMixin):
81
98
  # make it idempotent w/ a prompt
82
99
  text = text.text
83
100
  self._text = text
101
+ self.captured_variables = {}
84
102
 
85
103
  @classmethod
86
- def from_txt(cls, filename: str) -> PromptBase:
104
+ def from_txt(cls, filename: str) -> 'Prompt':
87
105
  """Create a `Prompt` from text.
88
106
 
89
107
  :param text: The text of the prompt.
@@ -98,7 +116,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
98
116
  file_name: str,
99
117
  path_to_folder: Optional[Union[str, Path]] = None,
100
118
  **kwargs: Dict[str, Any],
101
- ) -> "PromptBase":
119
+ ) -> "Prompt":
102
120
  """Create a `PromptBase` from a Jinja template.
103
121
 
104
122
  Args:
@@ -131,11 +149,11 @@ class Prompt(PersistenceMixin, RepresentationMixin):
131
149
  return cls(text=text)
132
150
 
133
151
  @property
134
- def text(self):
152
+ def text(self) -> str:
135
153
  """Return the `Prompt` text."""
136
154
  return self._text
137
155
 
138
- def __add__(self, other_prompt):
156
+ def __add__(self, other_prompt) -> 'Prompt':
139
157
  """Add two prompts together.
140
158
 
141
159
  Example:
@@ -163,7 +181,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
163
181
  """
164
182
  return self.text
165
183
 
166
- def __contains__(self, text_to_check):
184
+ def __contains__(self, text_to_check) -> bool:
167
185
  """Check if the text_to_check is in the `Prompt` text.
168
186
 
169
187
  Example:
@@ -190,7 +208,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
190
208
  """Return the variables in the template."""
191
209
  return _find_template_variables(self.text)
192
210
 
193
- def undefined_template_variables(self, replacement_dict: dict):
211
+ def undefined_template_variables(self, replacement_dict: dict) -> list[str]:
194
212
  """Return the variables in the template that are not in the replacement_dict.
195
213
 
196
214
  :param replacement_dict: A dictionary of replacements to populate the template.
@@ -207,7 +225,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
207
225
  """
208
226
  return [var for var in self.template_variables() if var not in replacement_dict]
209
227
 
210
- def unused_traits(self, traits: dict):
228
+ def unused_traits(self, traits: dict) -> list[str]:
211
229
  """Return the traits that are not used in the template."""
212
230
  return [trait for trait in traits if trait not in self.template_variables()]
213
231
 
@@ -227,8 +245,9 @@ class Prompt(PersistenceMixin, RepresentationMixin):
227
245
  """
228
246
  return len(self.template_variables()) > 0
229
247
 
230
- def render(self, primary_replacement: dict, **additional_replacements) -> str:
231
- """Render the prompt with the replacements.
248
+ def render(self, primary_replacement: dict, **additional_replacements) -> "Prompt":
249
+ """
250
+ Render the prompt with the replacements.
232
251
 
233
252
  :param primary_replacement: The primary replacement dictionary.
234
253
  :param additional_replacements: Additional replacement dictionaries.
@@ -245,57 +264,65 @@ class Prompt(PersistenceMixin, RepresentationMixin):
245
264
 
246
265
  >>> p.render({"person": "Mr. {{last_name}}"})
247
266
  Prompt(text=\"""Hello, Mr. {{ last_name }}\""")
267
+
268
+ >>> p = Prompt("The sum is {% set x = 2 + 3 %}{{ vars.set('x', x) }}{{x}}")
269
+ >>> result = p.render({})
270
+ >>> print(result.captured_variables)
271
+ {'x': 5}
272
+ >>> result.captured_variables['x']
273
+ 5
248
274
  """
249
275
  try:
250
- new_text = self._render(
251
- self.text, primary_replacement, **additional_replacements
276
+ template_vars = TemplateVars()
277
+ new_text, captured_vars = self._render(
278
+ self.text, primary_replacement, template_vars, **additional_replacements
252
279
  )
253
- return self.__class__(text=new_text)
280
+ result = Prompt(text=new_text)
281
+ result.captured_variables = captured_vars
282
+ return result
254
283
  except Exception as e:
255
284
  print(f"Error rendering prompt: {e}")
256
285
  return self
257
286
 
258
287
  @staticmethod
259
- def _render(text: str, primary_replacement, **additional_replacements) -> "PromptBase":
260
- """Render the template text with variables replaced."""
261
- import time
262
-
263
- # if there are no replacements, return the text
264
- if not primary_replacement and not additional_replacements:
265
- return text
266
-
267
- try:
268
- variables = _find_template_variables(text)
269
-
270
- if not variables: # if there are no variables, return the text
271
- return text
272
-
273
- # Combine all replacements
274
- all_replacements = {**primary_replacement, **additional_replacements}
275
-
276
- previous_text = None
277
- current_text = text
278
- iteration = 0
279
-
280
- for _ in range(MAX_NESTING):
281
- iteration += 1
282
-
283
- template = _compile_template(current_text)
284
- rendered_text = template.render(all_replacements)
285
-
286
- if rendered_text == current_text:
287
- return rendered_text
288
-
289
- previous_text = current_text
290
- current_text = rendered_text
291
-
292
- raise TemplateRenderError(
293
- "Too much nesting - you created an infinite loop here, pal"
294
- )
295
- except TemplateSyntaxError as e:
296
- raise TemplateRenderError(
297
- f"Template syntax error: {e}. Bad template: {text}"
298
- )
288
+ def _render(
289
+ text: str,
290
+ primary_replacement: dict,
291
+ template_vars: TemplateVars,
292
+ **additional_replacements
293
+ ) -> tuple[str, Dict[str, Any]]:
294
+ """
295
+ Render the template text with variables replaced.
296
+ Returns (rendered_text, captured_variables).
297
+ """
298
+ # Combine replacements.
299
+ all_replacements = {**primary_replacement, **additional_replacements}
300
+
301
+ # If no replacements and no Jinja variables, just return the text.
302
+ if not all_replacements and not _find_template_variables(text):
303
+ return text, template_vars.get_all()
304
+
305
+ env = make_env()
306
+ # Provide access to the 'vars' object inside the template.
307
+ env.globals['vars'] = template_vars
308
+
309
+ previous_text = None
310
+ current_text = text
311
+
312
+ for _ in range(MAX_NESTING):
313
+ template = env.from_string(current_text)
314
+ rendered_text = template.render(**all_replacements)
315
+
316
+ if rendered_text == current_text:
317
+ # No more changes, return final text with captured variables.
318
+ return rendered_text, template_vars.get_all()
319
+
320
+ previous_text = current_text
321
+ current_text = rendered_text
322
+
323
+ raise TemplateRenderError(
324
+ "Too much nesting - you created an infinite loop here, pal"
325
+ )
299
326
 
300
327
  def to_dict(self, add_edsl_version=False) -> dict[str, Any]:
301
328
  """Return the `Prompt` as a dictionary.
@@ -310,7 +337,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
310
337
  return {"text": self.text, "class_name": self.__class__.__name__}
311
338
 
312
339
  @classmethod
313
- def from_dict(cls, data) -> PromptBase:
340
+ def from_dict(cls, data) -> 'Prompt':
314
341
  """Create a `Prompt` from a dictionary.
315
342
 
316
343
  Example:
@@ -324,18 +351,6 @@ class Prompt(PersistenceMixin, RepresentationMixin):
324
351
  # class_name = data["class_name"]
325
352
  return Prompt(text=data["text"])
326
353
 
327
- # def rich_print(self):
328
- # """Display an object as a table."""
329
- # table = Table(title="Prompt")
330
- # table.add_column("Attribute", style="bold")
331
- # table.add_column("Value")
332
-
333
- # to_display = self.__dict__.copy()
334
- # for attr_name, attr_value in to_display.items():
335
- # table.add_row(attr_name, repr(attr_value))
336
- # table.add_row("Component type", str(self.component_type))
337
- # table.add_row("Model", str(getattr(self, "model", "Not specified")))
338
- # return table
339
354
 
340
355
  @classmethod
341
356
  def example(cls):
@@ -343,7 +358,12 @@ class Prompt(PersistenceMixin, RepresentationMixin):
343
358
  return cls(cls.default_instructions)
344
359
 
345
360
  def get_prompts(self) -> Dict[str, Any]:
346
- """Get the prompts for the question."""
361
+ """Get the prompts for the question.
362
+
363
+ >>> p = Prompt("Hello, {{person}}")
364
+ """
365
+
366
+ raise NotImplementedError("This method should be implemented by the subclass.")
347
367
  start = time.time()
348
368
 
349
369
  # Build all the components
@@ -396,20 +416,6 @@ class Prompt(PersistenceMixin, RepresentationMixin):
396
416
  return prompts
397
417
 
398
418
  if __name__ == "__main__":
399
- print("Running doctests...")
400
419
  import doctest
401
-
402
420
  doctest.testmod()
403
421
 
404
- # from edsl.prompts.library.question_multiple_choice import *
405
- # from edsl.prompts.library.agent_instructions import *
406
- # from edsl.prompts.library.agent_persona import *
407
-
408
- # from edsl.prompts.library.question_budget import *
409
- # from edsl.prompts.library.question_checkbox import *
410
- # from edsl.prompts.library.question_freetext import *
411
- # from edsl.prompts.library.question_linear_scale import *
412
- # from edsl.prompts.library.question_numerical import *
413
- # from edsl.prompts.library.question_rank import *
414
- # from edsl.prompts.library.question_extract import *
415
- # from edsl.prompts.library.question_list import *
@@ -1,5 +1,5 @@
1
1
  from typing import Optional
2
- from edsl.prompts.Prompt import Prompt
2
+ from ..prompts import Prompt
3
3
 
4
4
 
5
5
  class HTMLQuestion: