edsl 0.1.28__tar.gz → 0.1.29__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.
Files changed (210) hide show
  1. {edsl-0.1.28 → edsl-0.1.29}/PKG-INFO +11 -10
  2. {edsl-0.1.28 → edsl-0.1.29}/README.md +10 -10
  3. edsl-0.1.29/edsl/Base.py +288 -0
  4. edsl-0.1.29/edsl/__init__.py +42 -0
  5. edsl-0.1.29/edsl/__version__.py +1 -0
  6. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/Agent.py +77 -41
  7. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/AgentList.py +35 -6
  8. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/Invigilator.py +19 -1
  9. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/InvigilatorBase.py +15 -10
  10. edsl-0.1.29/edsl/agents/PromptConstructionMixin.py +376 -0
  11. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/descriptors.py +2 -1
  12. {edsl-0.1.28/edsl → edsl-0.1.29/edsl/base}/Base.py +2 -1
  13. {edsl-0.1.28 → edsl-0.1.29}/edsl/config.py +2 -1
  14. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputData.py +39 -8
  15. {edsl-0.1.28 → edsl-0.1.29}/edsl/coop/coop.py +188 -151
  16. edsl-0.1.29/edsl/coop/utils.py +123 -0
  17. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/Cache.py +19 -5
  18. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/SQLiteDict.py +11 -3
  19. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/Answers.py +15 -1
  20. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/Jobs.py +92 -47
  21. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/buckets/ModelBuckets.py +4 -2
  22. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/buckets/TokenBucket.py +1 -2
  23. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/Interview.py +3 -9
  24. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
  25. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewTaskBuildingMixin.py +15 -10
  26. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/runners/JobsRunnerAsyncio.py +21 -25
  27. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/TaskHistory.py +4 -3
  28. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/LanguageModel.py +5 -11
  29. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/ModelList.py +3 -3
  30. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/repair.py +8 -7
  31. {edsl-0.1.28 → edsl-0.1.29}/edsl/notebooks/Notebook.py +40 -3
  32. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/Prompt.py +31 -19
  33. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionBase.py +38 -13
  34. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionBudget.py +5 -6
  35. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionCheckBox.py +7 -3
  36. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionExtract.py +5 -3
  37. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionFreeText.py +3 -3
  38. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionFunctional.py +0 -3
  39. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionList.py +3 -4
  40. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionMultipleChoice.py +16 -8
  41. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionNumerical.py +4 -3
  42. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/QuestionRank.py +5 -3
  43. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/__init__.py +4 -3
  44. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/descriptors.py +4 -2
  45. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/question_registry.py +20 -31
  46. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/settings.py +1 -1
  47. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/Dataset.py +31 -0
  48. edsl-0.1.28/edsl/results/ResultsExportMixin.py → edsl-0.1.29/edsl/results/DatasetExportMixin.py +25 -90
  49. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/Result.py +22 -74
  50. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/Results.py +105 -67
  51. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/ResultsDBMixin.py +7 -3
  52. edsl-0.1.29/edsl/results/ResultsExportMixin.py +43 -0
  53. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/ResultsGGMixin.py +3 -3
  54. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/ResultsToolsMixin.py +5 -5
  55. edsl-0.1.29/edsl/scenarios/FileStore.py +140 -0
  56. {edsl-0.1.28 → edsl-0.1.29}/edsl/scenarios/Scenario.py +5 -6
  57. {edsl-0.1.28 → edsl-0.1.29}/edsl/scenarios/ScenarioList.py +44 -15
  58. edsl-0.1.29/edsl/scenarios/ScenarioListExportMixin.py +32 -0
  59. {edsl-0.1.28 → edsl-0.1.29}/edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  60. edsl-0.1.29/edsl/scenarios/__init__.py +2 -0
  61. edsl-0.1.29/edsl/study/ObjectEntry.py +173 -0
  62. {edsl-0.1.28 → edsl-0.1.29}/edsl/study/ProofOfWork.py +5 -2
  63. {edsl-0.1.28 → edsl-0.1.29}/edsl/study/SnapShot.py +4 -8
  64. {edsl-0.1.28 → edsl-0.1.29}/edsl/study/Study.py +21 -14
  65. {edsl-0.1.28 → edsl-0.1.29}/edsl/study/__init__.py +2 -0
  66. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/MemoryPlan.py +11 -4
  67. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/Survey.py +46 -7
  68. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/SurveyExportMixin.py +4 -2
  69. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  70. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/plotting.py +4 -2
  71. edsl-0.1.29/edsl/utilities/__init__.py +22 -0
  72. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/interface.py +66 -45
  73. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/utilities.py +11 -13
  74. {edsl-0.1.28 → edsl-0.1.29}/pyproject.toml +1 -4
  75. edsl-0.1.28/edsl/__init__.py +0 -42
  76. edsl-0.1.28/edsl/__version__.py +0 -1
  77. edsl-0.1.28/edsl/agents/PromptConstructionMixin.py +0 -134
  78. edsl-0.1.28/edsl/coop/utils.py +0 -155
  79. edsl-0.1.28/edsl/scenarios/__init__.py +0 -1
  80. edsl-0.1.28/edsl/study/ObjectEntry.py +0 -97
  81. edsl-0.1.28/edsl/utilities/__init__.py +0 -22
  82. {edsl-0.1.28 → edsl-0.1.29}/LICENSE +0 -0
  83. {edsl-0.1.28 → edsl-0.1.29}/edsl/BaseDiff.py +0 -0
  84. {edsl-0.1.28 → edsl-0.1.29}/edsl/agents/__init__.py +0 -0
  85. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/AgentConstructionMixin.py +0 -0
  86. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/Conjure.py +0 -0
  87. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputDataCSV.py +0 -0
  88. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputDataMixinQuestionStats.py +0 -0
  89. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputDataPyRead.py +0 -0
  90. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputDataSPSS.py +0 -0
  91. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/InputDataStata.py +0 -0
  92. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/QuestionOptionMixin.py +0 -0
  93. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/QuestionTypeMixin.py +0 -0
  94. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/RawQuestion.py +0 -0
  95. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/SurveyResponses.py +0 -0
  96. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/__init__.py +0 -0
  97. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/examples/placeholder.txt +0 -0
  98. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/naming_utilities.py +0 -0
  99. {edsl-0.1.28 → edsl-0.1.29}/edsl/conjure/utilities.py +0 -0
  100. {edsl-0.1.28 → edsl-0.1.29}/edsl/conversation/Conversation.py +0 -0
  101. {edsl-0.1.28 → edsl-0.1.29}/edsl/conversation/car_buying.py +0 -0
  102. {edsl-0.1.28 → edsl-0.1.29}/edsl/conversation/mug_negotiation.py +0 -0
  103. {edsl-0.1.28 → edsl-0.1.29}/edsl/conversation/next_speaker_utilities.py +0 -0
  104. {edsl-0.1.28 → edsl-0.1.29}/edsl/coop/__init__.py +0 -0
  105. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/CacheEntry.py +0 -0
  106. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/CacheHandler.py +0 -0
  107. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/__init__.py +0 -0
  108. {edsl-0.1.28 → edsl-0.1.29}/edsl/data/orm.py +0 -0
  109. {edsl-0.1.28 → edsl-0.1.29}/edsl/data_transfer_models.py +0 -0
  110. {edsl-0.1.28 → edsl-0.1.29}/edsl/enums.py +0 -0
  111. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/__init__.py +0 -0
  112. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/agents.py +0 -0
  113. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/configuration.py +0 -0
  114. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/coop.py +0 -0
  115. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/data.py +0 -0
  116. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/general.py +0 -0
  117. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/jobs.py +0 -0
  118. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/language_models.py +0 -0
  119. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/prompts.py +0 -0
  120. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/questions.py +0 -0
  121. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/results.py +0 -0
  122. {edsl-0.1.28 → edsl-0.1.29}/edsl/exceptions/surveys.py +0 -0
  123. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/AnthropicService.py +0 -0
  124. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/DeepInfraService.py +0 -0
  125. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/GoogleService.py +0 -0
  126. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/InferenceServiceABC.py +0 -0
  127. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/InferenceServicesCollection.py +0 -0
  128. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/OpenAIService.py +0 -0
  129. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/__init__.py +0 -0
  130. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/models_available_cache.py +0 -0
  131. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/rate_limits_cache.py +0 -0
  132. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/registry.py +0 -0
  133. {edsl-0.1.28 → edsl-0.1.29}/edsl/inference_services/write_available.py +0 -0
  134. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/__init__.py +0 -0
  135. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/buckets/BucketCollection.py +0 -0
  136. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewStatistic.py +0 -0
  137. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -0
  138. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewStatusDictionary.py +0 -0
  139. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/InterviewStatusLog.py +0 -0
  140. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/ReportErrors.py +0 -0
  141. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/interview_exception_tracking.py +0 -0
  142. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/interview_status_enum.py +0 -0
  143. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/interviews/retry_management.py +0 -0
  144. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  145. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/runners/JobsRunnerStatusMixin.py +0 -0
  146. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/QuestionTaskCreator.py +0 -0
  147. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/TaskCreators.py +0 -0
  148. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/TaskStatusLog.py +0 -0
  149. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/task_management.py +0 -0
  150. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tasks/task_status_enum.py +0 -0
  151. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tokens/InterviewTokenUsage.py +0 -0
  152. {edsl-0.1.28 → edsl-0.1.29}/edsl/jobs/tokens/TokenUsage.py +0 -0
  153. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/RegisterLanguageModelsMeta.py +0 -0
  154. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/__init__.py +0 -0
  155. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/registry.py +0 -0
  156. {edsl-0.1.28 → edsl-0.1.29}/edsl/language_models/unused/ReplicateBase.py +0 -0
  157. {edsl-0.1.28 → edsl-0.1.29}/edsl/notebooks/__init__.py +0 -0
  158. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/QuestionInstructionsBase.py +0 -0
  159. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/__init__.py +0 -0
  160. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/agent_instructions.py +0 -0
  161. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/agent_persona.py +0 -0
  162. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_budget.py +0 -0
  163. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_checkbox.py +0 -0
  164. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_extract.py +0 -0
  165. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_freetext.py +0 -0
  166. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_linear_scale.py +0 -0
  167. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_list.py +0 -0
  168. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_multiple_choice.py +0 -0
  169. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_numerical.py +0 -0
  170. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/library/question_rank.py +0 -0
  171. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/prompt_config.py +0 -0
  172. {edsl-0.1.28 → edsl-0.1.29}/edsl/prompts/registry.py +0 -0
  173. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/AnswerValidatorMixin.py +0 -0
  174. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/RegisterQuestionsMeta.py +0 -0
  175. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/SimpleAskMixin.py +0 -0
  176. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/compose_questions.py +0 -0
  177. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/derived/QuestionLikertFive.py +0 -0
  178. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/derived/QuestionLinearScale.py +0 -0
  179. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/derived/QuestionTopK.py +0 -0
  180. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/derived/QuestionYesNo.py +0 -0
  181. {edsl-0.1.28 → edsl-0.1.29}/edsl/questions/derived/__init__.py +0 -0
  182. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/ResultsFetchMixin.py +0 -0
  183. {edsl-0.1.28 → edsl-0.1.29}/edsl/results/__init__.py +0 -0
  184. {edsl-0.1.28 → edsl-0.1.29}/edsl/scenarios/ScenarioHtmlMixin.py +0 -0
  185. {edsl-0.1.28 → edsl-0.1.29}/edsl/scenarios/ScenarioImageMixin.py +0 -0
  186. {edsl-0.1.28 → edsl-0.1.29}/edsl/shared.py +0 -0
  187. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/DAG.py +0 -0
  188. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/Memory.py +0 -0
  189. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/Rule.py +0 -0
  190. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/RuleCollection.py +0 -0
  191. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/SurveyCSS.py +0 -0
  192. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/__init__.py +0 -0
  193. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/base.py +0 -0
  194. {edsl-0.1.28 → edsl-0.1.29}/edsl/surveys/descriptors.py +0 -0
  195. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/__init__.py +0 -0
  196. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/clusters.py +0 -0
  197. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/embeddings.py +0 -0
  198. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/embeddings_plotting.py +0 -0
  199. {edsl-0.1.28 → edsl-0.1.29}/edsl/tools/summarize.py +0 -0
  200. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/SystemInfo.py +0 -0
  201. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/ast_utilities.py +0 -0
  202. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/data/Registry.py +0 -0
  203. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/data/__init__.py +0 -0
  204. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/data/scooter_results.json +0 -0
  205. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/decorators.py +0 -0
  206. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/gcp_bucket/__init__.py +0 -0
  207. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/gcp_bucket/cloud_storage.py +0 -0
  208. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/gcp_bucket/simple_example.py +0 -0
  209. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/repair_functions.py +0 -0
  210. {edsl-0.1.28 → edsl-0.1.29}/edsl/utilities/restricted_python.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edsl
3
- Version: 0.1.28
3
+ Version: 0.1.29
4
4
  Summary: Create and analyze LLM-based surveys
5
5
  Home-page: https://www.expectedparrot.com/
6
6
  License: MIT
@@ -57,15 +57,6 @@ The Expected Parrot Domain-Specific Language (EDSL) package lets you conduct com
57
57
  - [LinkedIn](https://www.linkedin.com/company/expectedparrot/)
58
58
  - [Blog](https://blog.expectedparrot.com)
59
59
 
60
- ## 💡 Contributions, feature requests & bugs
61
- Interested in contributing? Want us to add a new feature? Found a bug for us to squash?
62
- Please send us an email at [info@expectedparrot.com](mailto:info@expectedparrot.com) or message us at our [Discord channel](https://discord.com/invite/mxAYkjfy9m).
63
-
64
- ## 💻 Requirements
65
- * EDSL is compatible with Python 3.9 - 3.12.
66
- * API keys for large language models that you want to use, stored in a `.env` file.
67
- See instructions on [storing API keys](https://docs.expectedparrot.com/en/latest/api_keys.html).
68
-
69
60
  ## 🌎 Hello, World!
70
61
  A quick example:
71
62
 
@@ -96,3 +87,13 @@ Output:
96
87
  │ Good │
97
88
  └───────────────────┘
98
89
  ```
90
+
91
+ ## 💻 Requirements
92
+ * EDSL is compatible with Python 3.9 - 3.12.
93
+ * API keys for large language models that you want to use, stored in a `.env` file.
94
+ See instructions on [storing API keys](https://docs.expectedparrot.com/en/latest/api_keys.html).
95
+
96
+ ## 💡 Contributions, feature requests & bugs
97
+ Interested in contributing? Want us to add a new feature? Found a bug for us to squash?
98
+ Please send us an email at [info@expectedparrot.com](mailto:info@expectedparrot.com) or message us at our [Discord channel](https://discord.com/invite/mxAYkjfy9m).
99
+
@@ -14,15 +14,6 @@ The Expected Parrot Domain-Specific Language (EDSL) package lets you conduct com
14
14
  - [LinkedIn](https://www.linkedin.com/company/expectedparrot/)
15
15
  - [Blog](https://blog.expectedparrot.com)
16
16
 
17
- ## 💡 Contributions, feature requests & bugs
18
- Interested in contributing? Want us to add a new feature? Found a bug for us to squash?
19
- Please send us an email at [info@expectedparrot.com](mailto:info@expectedparrot.com) or message us at our [Discord channel](https://discord.com/invite/mxAYkjfy9m).
20
-
21
- ## 💻 Requirements
22
- * EDSL is compatible with Python 3.9 - 3.12.
23
- * API keys for large language models that you want to use, stored in a `.env` file.
24
- See instructions on [storing API keys](https://docs.expectedparrot.com/en/latest/api_keys.html).
25
-
26
17
  ## 🌎 Hello, World!
27
18
  A quick example:
28
19
 
@@ -52,4 +43,13 @@ Output:
52
43
  ┡━━━━━━━━━━━━━━━━━━━┩
53
44
  │ Good │
54
45
  └───────────────────┘
55
- ```
46
+ ```
47
+
48
+ ## 💻 Requirements
49
+ * EDSL is compatible with Python 3.9 - 3.12.
50
+ * API keys for large language models that you want to use, stored in a `.env` file.
51
+ See instructions on [storing API keys](https://docs.expectedparrot.com/en/latest/api_keys.html).
52
+
53
+ ## 💡 Contributions, feature requests & bugs
54
+ Interested in contributing? Want us to add a new feature? Found a bug for us to squash?
55
+ Please send us an email at [info@expectedparrot.com](mailto:info@expectedparrot.com) or message us at our [Discord channel](https://discord.com/invite/mxAYkjfy9m).
@@ -0,0 +1,288 @@
1
+ """Base class for all classes in the package. It provides rich printing and persistence of objects."""
2
+
3
+ from abc import ABC, abstractmethod, ABCMeta
4
+ import gzip
5
+ import io
6
+ import json
7
+ from typing import Any, Optional, Union
8
+ from uuid import UUID
9
+
10
+
11
+ class RichPrintingMixin:
12
+ """Mixin for rich printing and persistence of objects."""
13
+
14
+ def _for_console(self):
15
+ """Return a string representation of the object for console printing."""
16
+ from rich.console import Console
17
+
18
+ with io.StringIO() as buf:
19
+ console = Console(file=buf, record=True)
20
+ table = self.rich_print()
21
+ console.print(table)
22
+ return console.export_text()
23
+
24
+ def __str__(self):
25
+ """Return a string representation of the object for console printing."""
26
+ return self._for_console()
27
+
28
+ def print(self):
29
+ """Print the object to the console."""
30
+ from edsl.utilities.utilities import is_notebook
31
+
32
+ if is_notebook():
33
+ from IPython.display import display
34
+
35
+ display(self.rich_print())
36
+ else:
37
+ from rich.console import Console
38
+
39
+ console = Console()
40
+ console.print(self.rich_print())
41
+
42
+
43
+ class PersistenceMixin:
44
+ """Mixin for saving and loading objects to and from files."""
45
+
46
+ def push(
47
+ self,
48
+ description: Optional[str] = None,
49
+ visibility: Optional[str] = "unlisted",
50
+ ):
51
+ """Post the object to coop."""
52
+ from edsl.coop import Coop
53
+
54
+ c = Coop()
55
+ return c.create(self, description, visibility)
56
+
57
+ @classmethod
58
+ def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
59
+ """Pull the object from coop."""
60
+ from edsl.coop import Coop
61
+ from edsl.coop.utils import ObjectRegistry
62
+
63
+ object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
64
+ coop = Coop()
65
+ return coop.get(uuid, url, object_type)
66
+
67
+ @classmethod
68
+ def delete(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
69
+ """Delete the object from coop."""
70
+ from edsl.coop import Coop
71
+
72
+ coop = Coop()
73
+ return coop.delete(uuid, url)
74
+
75
+ @classmethod
76
+ def patch(
77
+ cls,
78
+ uuid: Optional[Union[str, UUID]] = None,
79
+ url: Optional[str] = None,
80
+ description: Optional[str] = None,
81
+ value: Optional[Any] = None,
82
+ visibility: Optional[str] = None,
83
+ ):
84
+ """
85
+ Patch an uploaded objects attributes.
86
+ - `description` changes the description of the object on Coop
87
+ - `value` changes the value of the object on Coop. **has to be an EDSL object**
88
+ - `visibility` changes the visibility of the object on Coop
89
+ """
90
+ from edsl.coop import Coop
91
+
92
+ coop = Coop()
93
+ return coop.patch(uuid, url, description, value, visibility)
94
+
95
+ @classmethod
96
+ def search(cls, query):
97
+ """Search for objects on coop."""
98
+ from edsl.coop import Coop
99
+
100
+ c = Coop()
101
+ return c.search(cls, query)
102
+
103
+ def save(self, filename, compress=True):
104
+ """Save the object to a file as zippped JSON.
105
+
106
+ >>> obj.save("obj.json.gz")
107
+
108
+ """
109
+ if filename.endswith("json.gz"):
110
+ import warnings
111
+
112
+ warnings.warn(
113
+ "Do not apply the file extensions. The filename should not end with 'json.gz'."
114
+ )
115
+ filename = filename[:-7]
116
+ if filename.endswith("json"):
117
+ filename = filename[:-4]
118
+ warnings.warn(
119
+ "Do not apply the file extensions. The filename should not end with 'json'."
120
+ )
121
+
122
+ if compress:
123
+ with gzip.open(filename + ".json.gz", "wb") as f:
124
+ f.write(json.dumps(self.to_dict()).encode("utf-8"))
125
+ else:
126
+ with open(filename + ".json", "w") as f:
127
+ f.write(json.dumps(self.to_dict()))
128
+
129
+ @staticmethod
130
+ def open_compressed_file(filename):
131
+ with gzip.open(filename, "rb") as f:
132
+ file_contents = f.read()
133
+ file_contents_decoded = file_contents.decode("utf-8")
134
+ d = json.loads(file_contents_decoded)
135
+ return d
136
+
137
+ @staticmethod
138
+ def open_regular_file(filename):
139
+ with open(filename, "r") as f:
140
+ d = json.loads(f.read())
141
+ return d
142
+
143
+ @classmethod
144
+ def load(cls, filename):
145
+ """Load the object from a file.
146
+
147
+ >>> obj = cls.load("obj.json.gz")
148
+
149
+ """
150
+
151
+ if filename.endswith("json.gz"):
152
+ d = cls.open_compressed_file(filename)
153
+ elif filename.endswith("json"):
154
+ d = cls.open_regular_file(filename)
155
+ else:
156
+ try:
157
+ d = cls.open_compressed_file(filename)
158
+ except:
159
+ d = cls.open_regular_file(filename)
160
+ finally:
161
+ raise ValueError("File must be a json or json.gz file")
162
+
163
+ return cls.from_dict(d)
164
+
165
+
166
+ class RegisterSubclassesMeta(ABCMeta):
167
+ """Metaclass for registering subclasses."""
168
+
169
+ _registry = {}
170
+
171
+ def __init__(cls, name, bases, nmspc):
172
+ """Register the class in the registry upon creation."""
173
+ super(RegisterSubclassesMeta, cls).__init__(name, bases, nmspc)
174
+ if cls.__name__ != "Base":
175
+ RegisterSubclassesMeta._registry[cls.__name__] = cls
176
+
177
+ @staticmethod
178
+ def get_registry():
179
+ """Return the registry of subclasses."""
180
+ return dict(RegisterSubclassesMeta._registry)
181
+
182
+
183
+ class DiffMethodsMixin:
184
+ def __sub__(self, other):
185
+ """Return the difference between two objects."""
186
+ from edsl.BaseDiff import BaseDiff
187
+
188
+ return BaseDiff(self, other)
189
+
190
+
191
+ class Base(
192
+ RichPrintingMixin,
193
+ PersistenceMixin,
194
+ DiffMethodsMixin,
195
+ ABC,
196
+ metaclass=RegisterSubclassesMeta,
197
+ ):
198
+ """Base class for all classes in the package."""
199
+
200
+ # def __getitem__(self, key):
201
+ # return getattr(self, key)
202
+
203
+ # @abstractmethod
204
+ # def _repr_html_(self) -> str:
205
+ # raise NotImplementedError("This method is not implemented yet.")
206
+
207
+ # @abstractmethod
208
+ # def _repr_(self) -> str:
209
+ # raise NotImplementedError("This method is not implemented yet.")
210
+
211
+ def keys(self):
212
+ """Return the keys of the object."""
213
+ _keys = list(self.to_dict().keys())
214
+ if "edsl_version" in _keys:
215
+ _keys.remove("edsl_version")
216
+ if "edsl_class_name" in _keys:
217
+ _keys.remove("edsl_class_name")
218
+ return _keys
219
+
220
+ def values(self):
221
+ """Return the values of the object."""
222
+ data = self.to_dict()
223
+ keys = self.keys()
224
+ return {data[key] for key in keys}
225
+
226
+ def _repr_html_(self):
227
+ from edsl.utilities.utilities import data_to_html
228
+
229
+ return data_to_html(self.to_dict())
230
+
231
+ # def html(self):
232
+ # html_string = self._repr_html_()
233
+ # import tempfile
234
+ # import webbrowser
235
+
236
+ # with tempfile.NamedTemporaryFile("w", delete=False, suffix=".html") as f:
237
+ # # print("Writing HTML to", f.name)
238
+ # f.write(html_string)
239
+ # webbrowser.open(f.name)
240
+
241
+ def __eq__(self, other):
242
+ """Return whether two objects are equal."""
243
+ import inspect
244
+
245
+ if not isinstance(other, self.__class__):
246
+ return False
247
+ if "sort" in inspect.signature(self._to_dict).parameters:
248
+ return self._to_dict(sort=True) == other._to_dict(sort=True)
249
+ else:
250
+ return self._to_dict() == other._to_dict()
251
+
252
+ @abstractmethod
253
+ def example():
254
+ """This method should be implemented by subclasses."""
255
+ raise NotImplementedError("This method is not implemented yet.")
256
+
257
+ @abstractmethod
258
+ def rich_print():
259
+ """This method should be implemented by subclasses."""
260
+ raise NotImplementedError("This method is not implemented yet.")
261
+
262
+ @abstractmethod
263
+ def to_dict():
264
+ """This method should be implemented by subclasses."""
265
+ raise NotImplementedError("This method is not implemented yet.")
266
+
267
+ @abstractmethod
268
+ def from_dict():
269
+ """This method should be implemented by subclasses."""
270
+ raise NotImplementedError("This method is not implemented yet.")
271
+
272
+ @abstractmethod
273
+ def code():
274
+ """This method should be implemented by subclasses."""
275
+ raise NotImplementedError("This method is not implemented yet.")
276
+
277
+ def show_methods(self, show_docstrings=True):
278
+ """Show the methods of the object."""
279
+ public_methods_with_docstrings = [
280
+ (method, getattr(self, method).__doc__)
281
+ for method in dir(self)
282
+ if callable(getattr(self, method)) and not method.startswith("_")
283
+ ]
284
+ if show_docstrings:
285
+ for method, documentation in public_methods_with_docstrings:
286
+ print(f"{method}: {documentation}")
287
+ else:
288
+ return [x[0] for x in public_methods_with_docstrings]
@@ -0,0 +1,42 @@
1
+ import os
2
+ import time
3
+
4
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
5
+ ROOT_DIR = os.path.dirname(BASE_DIR)
6
+
7
+ from edsl.__version__ import __version__
8
+ from edsl.config import Config, CONFIG
9
+ from edsl.agents.Agent import Agent
10
+ from edsl.agents.AgentList import AgentList
11
+ from edsl.questions import QuestionBase
12
+ from edsl.questions import QuestionMultipleChoice
13
+ from edsl.questions import QuestionBudget
14
+ from edsl.questions import QuestionCheckBox
15
+ from edsl.questions import QuestionExtract
16
+ from edsl.questions import QuestionFreeText
17
+ from edsl.questions import QuestionFunctional
18
+ from edsl.questions import QuestionLikertFive
19
+ from edsl.questions import QuestionList
20
+ from edsl.questions import QuestionLinearScale
21
+ from edsl.questions import QuestionNumerical
22
+ from edsl.questions import QuestionRank
23
+ from edsl.questions import QuestionTopK
24
+ from edsl.questions import QuestionYesNo
25
+ from edsl.questions.question_registry import Question
26
+ from edsl.scenarios import Scenario
27
+ from edsl.scenarios import ScenarioList
28
+
29
+ # from edsl.utilities.interface import print_dict_with_rich
30
+ from edsl.surveys.Survey import Survey
31
+ from edsl.language_models.registry import Model
32
+ from edsl.language_models.ModelList import ModelList
33
+ from edsl.results.Results import Results
34
+ from edsl.data.Cache import Cache
35
+ from edsl.data.CacheEntry import CacheEntry
36
+ from edsl.data.CacheHandler import set_session_cache, unset_session_cache
37
+ from edsl.shared import shared_globals
38
+ from edsl.jobs.Jobs import Jobs
39
+ from edsl.notebooks.Notebook import Notebook
40
+ from edsl.study.Study import Study
41
+ from edsl.conjure.Conjure import Conjure
42
+ from edsl.coop.coop import Coop
@@ -0,0 +1 @@
1
+ __version__ = "0.1.29"
@@ -5,27 +5,14 @@ import copy
5
5
  import inspect
6
6
  import types
7
7
  from typing import Any, Callable, Optional, Union, Dict, Sequence
8
-
9
- from rich.table import Table
10
-
11
8
  from edsl.Base import Base
12
- from edsl.questions.QuestionBase import QuestionBase
13
- from edsl.language_models import LanguageModel
14
- from edsl.surveys.MemoryPlan import MemoryPlan
9
+
15
10
  from edsl.exceptions.agents import (
16
11
  AgentCombinationError,
17
12
  AgentDirectAnswerFunctionError,
18
13
  AgentDynamicTraitsFunctionError,
19
14
  )
20
- from edsl.agents.Invigilator import (
21
- InvigilatorDebug,
22
- InvigilatorHuman,
23
- InvigilatorFunctional,
24
- InvigilatorAI,
25
- InvigilatorBase,
26
- )
27
- from edsl.language_models.registry import Model
28
- from edsl.scenarios import Scenario
15
+
29
16
  from edsl.agents.descriptors import (
30
17
  TraitsDescriptor,
31
18
  CodebookDescriptor,
@@ -38,10 +25,6 @@ from edsl.utilities.decorators import (
38
25
  remove_edsl_version,
39
26
  )
40
27
  from edsl.data_transfer_models import AgentResponseDict
41
- from edsl.prompts.library.agent_persona import AgentPersona
42
- from edsl.data.Cache import Cache
43
-
44
-
45
28
  from edsl.utilities.restricted_python import create_restricted_function
46
29
 
47
30
 
@@ -56,6 +39,7 @@ class Agent(Base):
56
39
  name = NameDescriptor()
57
40
  dynamic_traits_function_name = ""
58
41
  answer_question_directly_function_name = ""
42
+ has_dynamic_traits_function = False
59
43
 
60
44
  def __init__(
61
45
  self,
@@ -129,12 +113,16 @@ class Agent(Base):
129
113
 
130
114
  if self.dynamic_traits_function:
131
115
  self.dynamic_traits_function_name = self.dynamic_traits_function.__name__
116
+ self.has_dynamic_traits_function = True
117
+ else:
118
+ self.has_dynamic_traits_function = False
132
119
 
133
120
  if dynamic_traits_function_source_code:
134
121
  self.dynamic_traits_function_name = dynamic_traits_function_name
135
122
  self.dynamic_traits_function = create_restricted_function(
136
123
  dynamic_traits_function_name, dynamic_traits_function
137
124
  )
125
+
138
126
  if answer_question_directly_source_code:
139
127
  self.answer_question_directly_function_name = (
140
128
  answer_question_directly_function_name
@@ -151,6 +139,8 @@ class Agent(Base):
151
139
  self.current_question = None
152
140
 
153
141
  if traits_presentation_template is not None:
142
+ from edsl.prompts.library.agent_persona import AgentPersona
143
+
154
144
  self.traits_presentation_template = traits_presentation_template
155
145
  self.agent_persona = AgentPersona(text=self.traits_presentation_template)
156
146
 
@@ -159,7 +149,7 @@ class Agent(Base):
159
149
 
160
150
  This checks whether the dynamic traits function is valid.
161
151
  """
162
- if self.dynamic_traits_function is not None:
152
+ if self.has_dynamic_traits_function:
163
153
  sig = inspect.signature(self.dynamic_traits_function)
164
154
  if "question" in sig.parameters:
165
155
  if len(sig.parameters) > 1:
@@ -189,7 +179,7 @@ class Agent(Base):
189
179
  {'age': 10, 'hair': 'brown', 'height': 5.5}
190
180
 
191
181
  """
192
- if self.dynamic_traits_function is not None:
182
+ if self.has_dynamic_traits_function:
193
183
  sig = inspect.signature(self.dynamic_traits_function)
194
184
  if "question" in sig.parameters:
195
185
  return self.dynamic_traits_function(question=self.current_question)
@@ -271,8 +261,9 @@ class Agent(Base):
271
261
  def create_invigilator(
272
262
  self,
273
263
  *,
274
- question: QuestionBase,
275
- cache,
264
+ question: "QuestionBase",
265
+ cache: "Cache",
266
+ survey: Optional["Survey"] = None,
276
267
  scenario: Optional[Scenario] = None,
277
268
  model: Optional[LanguageModel] = None,
278
269
  debug: bool = False,
@@ -280,7 +271,7 @@ class Agent(Base):
280
271
  current_answers: Optional[dict] = None,
281
272
  iteration: int = 1,
282
273
  sidecar_model=None,
283
- ) -> InvigilatorBase:
274
+ ) -> "InvigilatorBase":
284
275
  """Create an Invigilator.
285
276
 
286
277
  An invigilator is an object that is responsible for administering a question to an agent.
@@ -294,6 +285,8 @@ class Agent(Base):
294
285
  An invigator is an object that is responsible for administering a question to an agent and
295
286
  recording the responses.
296
287
  """
288
+ from edsl import Model, Scenario
289
+
297
290
  cache = cache
298
291
  self.current_question = question
299
292
  model = model or Model()
@@ -301,6 +294,7 @@ class Agent(Base):
301
294
  invigilator = self._create_invigilator(
302
295
  question=question,
303
296
  scenario=scenario,
297
+ survey=survey,
304
298
  model=model,
305
299
  debug=debug,
306
300
  memory_plan=memory_plan,
@@ -314,12 +308,13 @@ class Agent(Base):
314
308
  async def async_answer_question(
315
309
  self,
316
310
  *,
317
- question: QuestionBase,
318
- cache: Cache,
319
- scenario: Optional[Scenario] = None,
320
- model: Optional[LanguageModel] = None,
311
+ question: "QuestionBase",
312
+ cache: "Cache",
313
+ scenario: Optional["Scenario"] = None,
314
+ survey: Optional["Survey"] = None,
315
+ model: Optional["LanguageModel"] = None,
321
316
  debug: bool = False,
322
- memory_plan: Optional[MemoryPlan] = None,
317
+ memory_plan: Optional["MemoryPlan"] = None,
323
318
  current_answers: Optional[dict] = None,
324
319
  iteration: int = 0,
325
320
  ) -> AgentResponseDict:
@@ -349,6 +344,7 @@ class Agent(Base):
349
344
  question=question,
350
345
  cache=cache,
351
346
  scenario=scenario,
347
+ survey=survey,
352
348
  model=model,
353
349
  debug=debug,
354
350
  memory_plan=memory_plan,
@@ -362,21 +358,35 @@ class Agent(Base):
362
358
 
363
359
  def _create_invigilator(
364
360
  self,
365
- question: QuestionBase,
366
- cache: Optional[Cache] = None,
367
- scenario: Optional[Scenario] = None,
368
- model: Optional[LanguageModel] = None,
361
+ question: "QuestionBase",
362
+ cache: Optional["Cache"] = None,
363
+ scenario: Optional["Scenario"] = None,
364
+ model: Optional["LanguageModel"] = None,
365
+ survey: Optional["Survey"] = None,
369
366
  debug: bool = False,
370
- memory_plan: Optional[MemoryPlan] = None,
367
+ memory_plan: Optional["MemoryPlan"] = None,
371
368
  current_answers: Optional[dict] = None,
372
369
  iteration: int = 0,
373
370
  sidecar_model=None,
374
- ) -> InvigilatorBase:
371
+ ) -> "InvigilatorBase":
375
372
  """Create an Invigilator."""
373
+ from edsl import Model
374
+ from edsl import Scenario
375
+
376
376
  model = model or Model()
377
377
  scenario = scenario or Scenario()
378
378
 
379
+ from edsl.agents.Invigilator import (
380
+ InvigilatorDebug,
381
+ InvigilatorHuman,
382
+ InvigilatorFunctional,
383
+ InvigilatorAI,
384
+ InvigilatorBase,
385
+ )
386
+
379
387
  if cache is None:
388
+ from edsl.data.Cache import Cache
389
+
380
390
  cache = Cache()
381
391
 
382
392
  if debug:
@@ -404,6 +414,7 @@ class Agent(Base):
404
414
  self,
405
415
  question=question,
406
416
  scenario=scenario,
417
+ survey=survey,
407
418
  model=model,
408
419
  memory_plan=memory_plan,
409
420
  current_answers=current_answers,
@@ -479,6 +490,29 @@ class Agent(Base):
479
490
  """
480
491
  return self.data == other.data
481
492
 
493
+ def __getattr__(self, name):
494
+ # This will be called only if 'name' is not found in the usual places
495
+ # breakpoint()
496
+ if name == "has_dynamic_traits_function":
497
+ return self.has_dynamic_traits_function
498
+
499
+ if name in self.traits:
500
+ return self.traits[name]
501
+ raise AttributeError(
502
+ f"'{type(self).__name__}' object has no attribute '{name}'"
503
+ )
504
+
505
+ def __getstate__(self):
506
+ state = self.__dict__.copy()
507
+ # Include any additional state that needs to be serialized
508
+ return state
509
+
510
+ def __setstate__(self, state):
511
+ self.__dict__.update(state)
512
+ # Ensure _traits is initialized if it's missing
513
+ if "_traits" not in self.__dict__:
514
+ self._traits = {}
515
+
482
516
  def print(self) -> None:
483
517
  from rich import print_json
484
518
  import json
@@ -535,9 +569,9 @@ class Agent(Base):
535
569
  if dynamic_traits_func:
536
570
  func = inspect.getsource(dynamic_traits_func)
537
571
  raw_data["dynamic_traits_function_source_code"] = func
538
- raw_data[
539
- "dynamic_traits_function_name"
540
- ] = self.dynamic_traits_function_name
572
+ raw_data["dynamic_traits_function_name"] = (
573
+ self.dynamic_traits_function_name
574
+ )
541
575
  if hasattr(self, "answer_question_directly"):
542
576
  raw_data.pop(
543
577
  "answer_question_directly", None
@@ -553,9 +587,9 @@ class Agent(Base):
553
587
  raw_data["answer_question_directly_source_code"] = inspect.getsource(
554
588
  answer_question_directly_func
555
589
  )
556
- raw_data[
557
- "answer_question_directly_function_name"
558
- ] = self.answer_question_directly_function_name
590
+ raw_data["answer_question_directly_function_name"] = (
591
+ self.answer_question_directly_function_name
592
+ )
559
593
 
560
594
  return raw_data
561
595
 
@@ -640,6 +674,8 @@ class Agent(Base):
640
674
  >>> a.rich_print()
641
675
  <rich.table.Table object at ...>
642
676
  """
677
+ from rich.table import Table
678
+
643
679
  table_data, column_names = self._table()
644
680
  table = Table(title=f"{self.__class__.__name__} Attributes")
645
681
  for column in column_names: