edsl 0.1.33__py3-none-any.whl → 0.1.33.dev1__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 (180) hide show
  1. edsl/Base.py +3 -9
  2. edsl/__init__.py +3 -8
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +8 -40
  5. edsl/agents/AgentList.py +0 -43
  6. edsl/agents/Invigilator.py +219 -135
  7. edsl/agents/InvigilatorBase.py +59 -148
  8. edsl/agents/{PromptConstructor.py → PromptConstructionMixin.py} +89 -138
  9. edsl/agents/__init__.py +0 -1
  10. edsl/config.py +56 -47
  11. edsl/coop/coop.py +7 -50
  12. edsl/data/Cache.py +1 -35
  13. edsl/data_transfer_models.py +38 -73
  14. edsl/enums.py +0 -4
  15. edsl/exceptions/language_models.py +1 -25
  16. edsl/exceptions/questions.py +5 -62
  17. edsl/exceptions/results.py +0 -4
  18. edsl/inference_services/AnthropicService.py +11 -13
  19. edsl/inference_services/AwsBedrock.py +17 -19
  20. edsl/inference_services/AzureAI.py +20 -37
  21. edsl/inference_services/GoogleService.py +12 -16
  22. edsl/inference_services/GroqService.py +0 -2
  23. edsl/inference_services/InferenceServiceABC.py +3 -58
  24. edsl/inference_services/OpenAIService.py +54 -48
  25. edsl/inference_services/models_available_cache.py +6 -0
  26. edsl/inference_services/registry.py +0 -6
  27. edsl/jobs/Answers.py +12 -10
  28. edsl/jobs/Jobs.py +21 -36
  29. edsl/jobs/buckets/BucketCollection.py +15 -24
  30. edsl/jobs/buckets/TokenBucket.py +14 -93
  31. edsl/jobs/interviews/Interview.py +78 -366
  32. edsl/jobs/interviews/InterviewExceptionEntry.py +19 -85
  33. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +286 -0
  34. edsl/jobs/interviews/{InterviewExceptionCollection.py → interview_exception_tracking.py} +68 -14
  35. edsl/jobs/interviews/retry_management.py +37 -0
  36. edsl/jobs/runners/JobsRunnerAsyncio.py +175 -146
  37. edsl/jobs/runners/JobsRunnerStatusMixin.py +333 -0
  38. edsl/jobs/tasks/QuestionTaskCreator.py +23 -30
  39. edsl/jobs/tasks/TaskHistory.py +213 -148
  40. edsl/language_models/LanguageModel.py +156 -261
  41. edsl/language_models/ModelList.py +2 -2
  42. edsl/language_models/RegisterLanguageModelsMeta.py +29 -14
  43. edsl/language_models/registry.py +6 -23
  44. edsl/language_models/repair.py +19 -0
  45. edsl/prompts/Prompt.py +2 -52
  46. edsl/questions/AnswerValidatorMixin.py +26 -23
  47. edsl/questions/QuestionBase.py +249 -329
  48. edsl/questions/QuestionBudget.py +41 -99
  49. edsl/questions/QuestionCheckBox.py +35 -227
  50. edsl/questions/QuestionExtract.py +27 -98
  51. edsl/questions/QuestionFreeText.py +29 -52
  52. edsl/questions/QuestionFunctional.py +0 -7
  53. edsl/questions/QuestionList.py +22 -141
  54. edsl/questions/QuestionMultipleChoice.py +65 -159
  55. edsl/questions/QuestionNumerical.py +46 -88
  56. edsl/questions/QuestionRank.py +24 -182
  57. edsl/questions/RegisterQuestionsMeta.py +12 -31
  58. edsl/questions/__init__.py +4 -3
  59. edsl/questions/derived/QuestionLikertFive.py +5 -10
  60. edsl/questions/derived/QuestionLinearScale.py +2 -15
  61. edsl/questions/derived/QuestionTopK.py +1 -10
  62. edsl/questions/derived/QuestionYesNo.py +3 -24
  63. edsl/questions/descriptors.py +7 -43
  64. edsl/questions/question_registry.py +2 -6
  65. edsl/results/Dataset.py +0 -20
  66. edsl/results/DatasetExportMixin.py +48 -46
  67. edsl/results/Result.py +5 -32
  68. edsl/results/Results.py +46 -135
  69. edsl/results/ResultsDBMixin.py +3 -3
  70. edsl/scenarios/FileStore.py +10 -71
  71. edsl/scenarios/Scenario.py +25 -96
  72. edsl/scenarios/ScenarioImageMixin.py +2 -2
  73. edsl/scenarios/ScenarioList.py +39 -361
  74. edsl/scenarios/ScenarioListExportMixin.py +0 -9
  75. edsl/scenarios/ScenarioListPdfMixin.py +4 -150
  76. edsl/study/SnapShot.py +1 -8
  77. edsl/study/Study.py +0 -32
  78. edsl/surveys/Rule.py +1 -10
  79. edsl/surveys/RuleCollection.py +5 -21
  80. edsl/surveys/Survey.py +310 -636
  81. edsl/surveys/SurveyExportMixin.py +9 -71
  82. edsl/surveys/SurveyFlowVisualizationMixin.py +1 -2
  83. edsl/surveys/SurveyQualtricsImport.py +4 -75
  84. edsl/utilities/gcp_bucket/simple_example.py +9 -0
  85. edsl/utilities/utilities.py +1 -9
  86. {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/METADATA +2 -5
  87. edsl-0.1.33.dev1.dist-info/RECORD +209 -0
  88. edsl/TemplateLoader.py +0 -24
  89. edsl/auto/AutoStudy.py +0 -117
  90. edsl/auto/StageBase.py +0 -230
  91. edsl/auto/StageGenerateSurvey.py +0 -178
  92. edsl/auto/StageLabelQuestions.py +0 -125
  93. edsl/auto/StagePersona.py +0 -61
  94. edsl/auto/StagePersonaDimensionValueRanges.py +0 -88
  95. edsl/auto/StagePersonaDimensionValues.py +0 -74
  96. edsl/auto/StagePersonaDimensions.py +0 -69
  97. edsl/auto/StageQuestions.py +0 -73
  98. edsl/auto/SurveyCreatorPipeline.py +0 -21
  99. edsl/auto/utilities.py +0 -224
  100. edsl/coop/PriceFetcher.py +0 -58
  101. edsl/inference_services/MistralAIService.py +0 -120
  102. edsl/inference_services/TestService.py +0 -80
  103. edsl/inference_services/TogetherAIService.py +0 -170
  104. edsl/jobs/FailedQuestion.py +0 -78
  105. edsl/jobs/runners/JobsRunnerStatus.py +0 -331
  106. edsl/language_models/fake_openai_call.py +0 -15
  107. edsl/language_models/fake_openai_service.py +0 -61
  108. edsl/language_models/utilities.py +0 -61
  109. edsl/questions/QuestionBaseGenMixin.py +0 -133
  110. edsl/questions/QuestionBasePromptsMixin.py +0 -266
  111. edsl/questions/Quick.py +0 -41
  112. edsl/questions/ResponseValidatorABC.py +0 -170
  113. edsl/questions/decorators.py +0 -21
  114. edsl/questions/prompt_templates/question_budget.jinja +0 -13
  115. edsl/questions/prompt_templates/question_checkbox.jinja +0 -32
  116. edsl/questions/prompt_templates/question_extract.jinja +0 -11
  117. edsl/questions/prompt_templates/question_free_text.jinja +0 -3
  118. edsl/questions/prompt_templates/question_linear_scale.jinja +0 -11
  119. edsl/questions/prompt_templates/question_list.jinja +0 -17
  120. edsl/questions/prompt_templates/question_multiple_choice.jinja +0 -33
  121. edsl/questions/prompt_templates/question_numerical.jinja +0 -37
  122. edsl/questions/templates/__init__.py +0 -0
  123. edsl/questions/templates/budget/__init__.py +0 -0
  124. edsl/questions/templates/budget/answering_instructions.jinja +0 -7
  125. edsl/questions/templates/budget/question_presentation.jinja +0 -7
  126. edsl/questions/templates/checkbox/__init__.py +0 -0
  127. edsl/questions/templates/checkbox/answering_instructions.jinja +0 -10
  128. edsl/questions/templates/checkbox/question_presentation.jinja +0 -22
  129. edsl/questions/templates/extract/__init__.py +0 -0
  130. edsl/questions/templates/extract/answering_instructions.jinja +0 -7
  131. edsl/questions/templates/extract/question_presentation.jinja +0 -1
  132. edsl/questions/templates/free_text/__init__.py +0 -0
  133. edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  134. edsl/questions/templates/free_text/question_presentation.jinja +0 -1
  135. edsl/questions/templates/likert_five/__init__.py +0 -0
  136. edsl/questions/templates/likert_five/answering_instructions.jinja +0 -10
  137. edsl/questions/templates/likert_five/question_presentation.jinja +0 -12
  138. edsl/questions/templates/linear_scale/__init__.py +0 -0
  139. edsl/questions/templates/linear_scale/answering_instructions.jinja +0 -5
  140. edsl/questions/templates/linear_scale/question_presentation.jinja +0 -5
  141. edsl/questions/templates/list/__init__.py +0 -0
  142. edsl/questions/templates/list/answering_instructions.jinja +0 -4
  143. edsl/questions/templates/list/question_presentation.jinja +0 -5
  144. edsl/questions/templates/multiple_choice/__init__.py +0 -0
  145. edsl/questions/templates/multiple_choice/answering_instructions.jinja +0 -9
  146. edsl/questions/templates/multiple_choice/html.jinja +0 -0
  147. edsl/questions/templates/multiple_choice/question_presentation.jinja +0 -12
  148. edsl/questions/templates/numerical/__init__.py +0 -0
  149. edsl/questions/templates/numerical/answering_instructions.jinja +0 -8
  150. edsl/questions/templates/numerical/question_presentation.jinja +0 -7
  151. edsl/questions/templates/rank/__init__.py +0 -0
  152. edsl/questions/templates/rank/answering_instructions.jinja +0 -11
  153. edsl/questions/templates/rank/question_presentation.jinja +0 -15
  154. edsl/questions/templates/top_k/__init__.py +0 -0
  155. edsl/questions/templates/top_k/answering_instructions.jinja +0 -8
  156. edsl/questions/templates/top_k/question_presentation.jinja +0 -22
  157. edsl/questions/templates/yes_no/__init__.py +0 -0
  158. edsl/questions/templates/yes_no/answering_instructions.jinja +0 -6
  159. edsl/questions/templates/yes_no/question_presentation.jinja +0 -12
  160. edsl/results/DatasetTree.py +0 -145
  161. edsl/results/Selector.py +0 -118
  162. edsl/results/tree_explore.py +0 -115
  163. edsl/surveys/instructions/ChangeInstruction.py +0 -47
  164. edsl/surveys/instructions/Instruction.py +0 -34
  165. edsl/surveys/instructions/InstructionCollection.py +0 -77
  166. edsl/surveys/instructions/__init__.py +0 -0
  167. edsl/templates/error_reporting/base.html +0 -24
  168. edsl/templates/error_reporting/exceptions_by_model.html +0 -35
  169. edsl/templates/error_reporting/exceptions_by_question_name.html +0 -17
  170. edsl/templates/error_reporting/exceptions_by_type.html +0 -17
  171. edsl/templates/error_reporting/interview_details.html +0 -116
  172. edsl/templates/error_reporting/interviews.html +0 -10
  173. edsl/templates/error_reporting/overview.html +0 -5
  174. edsl/templates/error_reporting/performance_plot.html +0 -2
  175. edsl/templates/error_reporting/report.css +0 -74
  176. edsl/templates/error_reporting/report.html +0 -118
  177. edsl/templates/error_reporting/report.js +0 -25
  178. edsl-0.1.33.dist-info/RECORD +0 -295
  179. {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/LICENSE +0 -0
  180. {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/WHEEL +0 -0
@@ -1,61 +0,0 @@
1
- import threading
2
- import asyncio
3
- from fastapi import FastAPI, Request
4
- from fastapi.responses import JSONResponse
5
- import uvicorn
6
- import json
7
- from typing import Any
8
-
9
- app = FastAPI()
10
-
11
-
12
- async def generate_response(question_number: int) -> dict:
13
- # Simulate some asynchronous work
14
- await asyncio.sleep(1)
15
- return {
16
- "id": "chatcmpl-123",
17
- "object": "chat.completion",
18
- "created": 1677652288,
19
- "model": "gpt-3.5-turbo-0613",
20
- "choices": [
21
- {
22
- "index": 0,
23
- "message": {
24
- "role": "assistant",
25
- "content": json.dumps(
26
- {"answer": f"SPAM for question {question_number}!"}
27
- ),
28
- },
29
- "finish_reason": "stop",
30
- }
31
- ],
32
- "usage": {"prompt_tokens": 9, "completion_tokens": 12, "total_tokens": 21},
33
- }
34
-
35
-
36
- @app.post("/v1/chat/completions")
37
- async def chat_completions(request: Request):
38
- body = await request.json()
39
- user_prompt = body["messages"][-1]["content"]
40
- question_number = int(user_prompt.split("XX")[1])
41
-
42
- response = await generate_response(question_number)
43
- return JSONResponse(content=response)
44
-
45
-
46
- def run_server():
47
- uvicorn.run(app, host="127.0.0.1", port=8000)
48
-
49
-
50
- if __name__ == "__main__":
51
- # Start the server in a separate thread
52
- server_thread = threading.Thread(target=run_server)
53
- server_thread.start()
54
-
55
- # Your main code here
56
- # ...
57
-
58
- # To use this with the OpenAI SDK:
59
- # from openai import AsyncOpenAI
60
- # client = AsyncOpenAI(base_url="http://127.0.0.1:8000/v1", api_key="fake_key")
61
- # response = await client.chat.completions.create(model="gpt-3.5-turbo", messages=[...])
@@ -1,61 +0,0 @@
1
- import asyncio
2
- from typing import Any
3
- from edsl import Survey
4
- from edsl.config import CONFIG
5
- from edsl.enums import InferenceServiceType
6
- from edsl.language_models.LanguageModel import LanguageModel
7
- from edsl.questions import QuestionFreeText
8
-
9
-
10
- def create_survey(num_questions: int, chained: bool = True, take_scenario=False):
11
- survey = Survey()
12
- for i in range(num_questions):
13
- if take_scenario:
14
- q = QuestionFreeText(
15
- question_text=f"XX{i}XX and {{scenario_value }}",
16
- question_name=f"question_{i}",
17
- )
18
- else:
19
- q = QuestionFreeText(
20
- question_text=f"XX{i}XX", question_name=f"question_{i}"
21
- )
22
- survey.add_question(q)
23
- if i > 0 and chained:
24
- survey.add_targeted_memory(f"question_{i}", f"question_{i-1}")
25
- return survey
26
-
27
-
28
- def create_language_model(
29
- exception: Exception, fail_at_number: int, never_ending=False
30
- ):
31
- class LanguageModelFromUtilities(LanguageModel):
32
- _model_ = "test"
33
- _parameters_ = {"temperature": 0.5}
34
- _inference_service_ = InferenceServiceType.TEST.value
35
- key_sequence = ["message", 0, "text"]
36
- usage_sequence = ["usage"]
37
- input_token_name = "prompt_tokens"
38
- output_token_name = "completion_tokens"
39
- _rpm = 1000000000000
40
- _tpm = 1000000000000
41
-
42
- async def async_execute_model_call(
43
- self, user_prompt: str, system_prompt: str
44
- ) -> dict[str, Any]:
45
- question_number = int(
46
- user_prompt.split("XX")[1]
47
- ) ## grabs the question number from the prompt
48
- await asyncio.sleep(0.1)
49
- if never_ending: ## you're not going anywhere buddy
50
- await asyncio.sleep(float("inf"))
51
- if question_number == fail_at_number:
52
- if asyncio.iscoroutinefunction(exception):
53
- await exception()
54
- else:
55
- raise exception
56
- return {
57
- "message": [{"text": "SPAM!"}],
58
- "usage": {"prompt_tokens": 1, "completion_tokens": 1},
59
- }
60
-
61
- return LanguageModelFromUtilities
@@ -1,133 +0,0 @@
1
- from __future__ import annotations
2
- import copy
3
- import itertools
4
- from typing import Optional, List, Callable, Type
5
- from typing import TypeVar
6
-
7
-
8
- class QuestionBaseGenMixin:
9
- def copy(self) -> QuestionBase:
10
- """Return a deep copy of the question.
11
-
12
- >>> from edsl.questions import QuestionFreeText
13
- >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
14
- >>> q2 = q.copy()
15
- >>> q2.question_name
16
- 'color'
17
-
18
- """
19
- return copy.deepcopy(self)
20
-
21
- def option_permutations(self) -> list[QuestionBase]:
22
- """Return a list of questions with all possible permutations of the options.
23
-
24
- >>> from edsl import QuestionMultipleChoice as Q
25
- >>> len(Q.example().option_permutations())
26
- 24
27
- """
28
-
29
- if not hasattr(self, "question_options"):
30
- return [self]
31
-
32
- questions = []
33
- for index, permutation in enumerate(
34
- itertools.permutations(self.question_options)
35
- ):
36
- question = copy.deepcopy(self)
37
- question.question_options = list(permutation)
38
- question.question_name = f"{self.question_name}_{index}"
39
- questions.append(question)
40
- return questions
41
-
42
- def loop(self, scenario_list: ScenarioList) -> List[QuestionBase]:
43
- """Return a list of questions with the question name modified for each scenario.
44
-
45
- :param scenario_list: The list of scenarios to loop through.
46
-
47
- >>> from edsl import QuestionFreeText
48
- >>> from edsl import ScenarioList
49
- >>> q = QuestionFreeText(question_text = "What are your thoughts on: {{ subject}}?", question_name = "base_{{subject}}")
50
- >>> len(q.loop(ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])))
51
- 3
52
-
53
- """
54
- from jinja2 import Environment
55
- from edsl.questions.QuestionBase import QuestionBase
56
-
57
- starting_name = self.question_name
58
- questions = []
59
- for index, scenario in enumerate(scenario_list):
60
- env = Environment()
61
- new_data = self.to_dict().copy()
62
- for key, value in [(k, v) for k, v in new_data.items() if v is not None]:
63
- if (
64
- isinstance(value, str) or isinstance(value, int)
65
- ) and key != "question_options":
66
- new_data[key] = env.from_string(value).render(scenario)
67
- elif isinstance(value, list):
68
- new_data[key] = [
69
- env.from_string(v).render(scenario) if isinstance(v, str) else v
70
- for v in value
71
- ]
72
- elif isinstance(value, dict):
73
- new_data[key] = {
74
- (
75
- env.from_string(k).render(scenario)
76
- if isinstance(k, str)
77
- else k
78
- ): (
79
- env.from_string(v).render(scenario)
80
- if isinstance(v, str)
81
- else v
82
- )
83
- for k, v in value.items()
84
- }
85
- elif key == "question_options" and isinstance(value, str):
86
- new_data[key] = value
87
- else:
88
- raise ValueError(
89
- f"Unexpected value type: {type(value)} for key '{key}'"
90
- )
91
-
92
- if new_data["question_name"] == starting_name:
93
- new_data["question_name"] = new_data["question_name"] + f"_{index}"
94
-
95
- questions.append(QuestionBase.from_dict(new_data))
96
- return questions
97
-
98
- def apply_function(self, func: Callable, exclude_components=None) -> QuestionBase:
99
- """Apply a function to the question parts
100
-
101
- >>> from edsl.questions import QuestionFreeText
102
- >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
103
- >>> shouting = lambda x: x.upper()
104
- >>> q.apply_function(shouting)
105
- Question('free_text', question_name = \"""color\""", question_text = \"""WHAT IS YOUR FAVORITE COLOR?\""")
106
-
107
- """
108
- from edsl.questions.QuestionBase import QuestionBase
109
-
110
- if exclude_components is None:
111
- exclude_components = ["question_name", "question_type"]
112
-
113
- d = copy.deepcopy(self._to_dict())
114
- for key, value in d.items():
115
- if key in exclude_components:
116
- continue
117
- if isinstance(value, dict):
118
- for k, v in value.items():
119
- value[k] = func(v)
120
- d[key] = value
121
- continue
122
- if isinstance(value, list):
123
- value = [func(v) for v in value]
124
- d[key] = value
125
- continue
126
- d[key] = func(value)
127
- return QuestionBase.from_dict(d)
128
-
129
-
130
- if __name__ == "__main__":
131
- import doctest
132
-
133
- doctest.testmod()
@@ -1,266 +0,0 @@
1
- from importlib import resources
2
- from typing import Optional
3
- from edsl.prompts import Prompt
4
- from edsl.exceptions.questions import QuestionAnswerValidationError
5
-
6
- from functools import lru_cache
7
-
8
-
9
- class TemplateManager:
10
- _instance = None
11
-
12
- def __new__(cls):
13
- if cls._instance is None:
14
- cls._instance = super().__new__(cls)
15
- cls._instance._template_cache = {}
16
- return cls._instance
17
-
18
- @lru_cache(maxsize=None)
19
- def get_template(self, question_type, template_name):
20
- if (question_type, template_name) not in self._template_cache:
21
- with resources.open_text(
22
- f"edsl.questions.templates.{question_type}", template_name
23
- ) as file:
24
- self._template_cache[(question_type, template_name)] = file.read()
25
- return self._template_cache[(question_type, template_name)]
26
-
27
-
28
- # Global instance
29
- template_manager = TemplateManager()
30
-
31
-
32
- class QuestionBasePromptsMixin:
33
- # @classmethod
34
- # @lru_cache(maxsize=1)
35
- # def _read_template(cls, template_name):
36
- # with resources.open_text(
37
- # f"edsl.questions.templates.{cls.question_type}", template_name
38
- # ) as file:
39
- # return file.read()
40
-
41
- @classmethod
42
- def applicable_prompts(
43
- cls, model: Optional[str] = None
44
- ) -> list[type["PromptBase"]]:
45
- """Get the prompts that are applicable to the question type.
46
-
47
- :param model: The language model to use.
48
-
49
- >>> from edsl.questions import QuestionFreeText
50
- >>> QuestionFreeText.applicable_prompts()
51
- [<class 'edsl.prompts.library.question_freetext.FreeText'>]
52
-
53
- :param model: The language model to use. If None, assumes does not matter.
54
-
55
- """
56
- from edsl.prompts.registry import get_classes as prompt_lookup
57
-
58
- applicable_prompts = prompt_lookup(
59
- component_type="question_instructions",
60
- question_type=cls.question_type,
61
- model=model,
62
- )
63
- return applicable_prompts
64
-
65
- @property
66
- def model_instructions(self) -> dict:
67
- """Get the model-specific instructions for the question."""
68
- if not hasattr(self, "_model_instructions"):
69
- self._model_instructions = {}
70
- return self._model_instructions
71
-
72
- def _all_text(self) -> str:
73
- """Return the question text.
74
-
75
- >>> from edsl import QuestionMultipleChoice as Q
76
- >>> Q.example()._all_text()
77
- "how_feelingHow are you?['Good', 'Great', 'OK', 'Bad']"
78
- """
79
- txt = ""
80
- for key, value in self.data.items():
81
- if isinstance(value, str):
82
- txt += value
83
- elif isinstance(value, list):
84
- txt += "".join(str(value))
85
- return txt
86
-
87
- @model_instructions.setter
88
- def model_instructions(self, data: dict):
89
- """Set the model-specific instructions for the question."""
90
- self._model_instructions = data
91
-
92
- def add_model_instructions(
93
- self, *, instructions: str, model: Optional[str] = None
94
- ) -> None:
95
- """Add model-specific instructions for the question that override the default instructions.
96
-
97
- :param instructions: The instructions to add. This is typically a jinja2 template.
98
- :param model: The language model for this instruction.
99
-
100
- >>> from edsl.questions import QuestionFreeText
101
- >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
102
- >>> q.add_model_instructions(instructions = "{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
103
- >>> q.get_instructions(model = "gpt3")
104
- Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
105
- """
106
- from edsl import Model
107
-
108
- if not hasattr(self, "_model_instructions"):
109
- self._model_instructions = {}
110
- if model is None:
111
- # if not model is passed, all the models are mapped to this instruction, including 'None'
112
- self._model_instructions = {
113
- model_name: instructions
114
- for model_name in Model.available(name_only=True)
115
- }
116
- self._model_instructions.update({model: instructions})
117
- else:
118
- self._model_instructions.update({model: instructions})
119
-
120
- @classmethod
121
- def path_to_folder(cls) -> str:
122
- return resources.files(f"edsl.questions.templates", cls.question_type)
123
-
124
- @property
125
- def response_model(self) -> type["BaseModel"]:
126
- if self._response_model is not None:
127
- return self._response_model
128
- else:
129
- return self.create_response_model()
130
-
131
- @property
132
- def use_code(self) -> bool:
133
- if hasattr(self, "_use_code"):
134
- return self._use_code
135
- return True
136
-
137
- @use_code.setter
138
- def use_code(self, value: bool) -> None:
139
- self._use_code = value
140
-
141
- @property
142
- def include_comment(self) -> bool:
143
- if hasattr(self, "_include_comment"):
144
- return self._include_comment
145
- return True
146
-
147
- @include_comment.setter
148
- def include_comment(self, value: bool) -> None:
149
- self._include_comment = value
150
-
151
- @classmethod
152
- def default_answering_instructions(cls) -> str:
153
- # template_text = cls._read_template("answering_instructions.jinja")
154
- template_text = template_manager.get_template(
155
- cls.question_type, "answering_instructions.jinja"
156
- )
157
- return Prompt(text=template_text)
158
-
159
- @classmethod
160
- def default_question_presentation(cls):
161
- # template_text = cls._read_template("question_presentation.jinja")
162
- template_text = template_manager.get_template(
163
- cls.question_type, "question_presentation.jinja"
164
- )
165
- return Prompt(text=template_text)
166
-
167
- @property
168
- def answering_instructions(self) -> str:
169
- if self._answering_instructions is None:
170
- return self.default_answering_instructions()
171
- return self._answering_instructions
172
-
173
- @answering_instructions.setter
174
- def answering_instructions(self, value) -> None:
175
- self._answering_instructions = value
176
-
177
- # @classmethod
178
- # def default_answering_instructions(cls) -> str:
179
- # with resources.open_text(
180
- # f"edsl.questions.templates.{cls.question_type}",
181
- # "answering_instructions.jinja",
182
- # ) as file:
183
- # return Prompt(text=file.read())
184
-
185
- # @classmethod
186
- # def default_question_presentation(cls):
187
- # with resources.open_text(
188
- # f"edsl.questions.templates.{cls.question_type}",
189
- # "question_presentation.jinja",
190
- # ) as file:
191
- # return Prompt(text=file.read())
192
-
193
- @property
194
- def question_presentation(self):
195
- if self._question_presentation is None:
196
- return self.default_question_presentation()
197
- return self._question_presentation
198
-
199
- @question_presentation.setter
200
- def question_presentation(self, value):
201
- self._question_presentation = value
202
-
203
- def prompt_preview(self, scenario=None, agent=None):
204
- return self.new_default_instructions.render(
205
- self.data
206
- | {
207
- "include_comment": getattr(self, "_include_comment", True),
208
- "use_code": getattr(self, "_use_code", True),
209
- }
210
- | ({"scenario": scenario} or {})
211
- | ({"agent": agent} or {})
212
- )
213
-
214
- @classmethod
215
- def self_check(cls):
216
- q = cls.example()
217
- for answer, params in q.response_validator.valid_examples:
218
- for key, value in params.items():
219
- setattr(q, key, value)
220
- q._validate_answer(answer)
221
- for answer, params, reason in q.response_validator.invalid_examples:
222
- for key, value in params.items():
223
- setattr(q, key, value)
224
- try:
225
- q._validate_answer(answer)
226
- except QuestionAnswerValidationError:
227
- pass
228
- else:
229
- raise ValueError(f"Example {answer} should have failed for {reason}.")
230
-
231
- @property
232
- def new_default_instructions(self) -> "Prompt":
233
- "This is set up as a property because there are mutable question values that determine how it is rendered."
234
- return self.question_presentation + self.answering_instructions
235
-
236
- @property
237
- def parameters(self) -> set[str]:
238
- """Return the parameters of the question."""
239
- from jinja2 import Environment, meta
240
-
241
- env = Environment()
242
- # Parse the template
243
- txt = self._all_text()
244
- # txt = self.question_text
245
- # if hasattr(self, "question_options"):
246
- # txt += " ".join(self.question_options)
247
- parsed_content = env.parse(txt)
248
- # Extract undeclared variables
249
- variables = meta.find_undeclared_variables(parsed_content)
250
- # Return as a list
251
- return set(variables)
252
-
253
- def get_instructions(self, model: Optional[str] = None) -> type["PromptBase"]:
254
- """Get the mathcing question-answering instructions for the question.
255
-
256
- :param model: The language model to use.
257
- """
258
- from edsl.prompts.Prompt import Prompt
259
-
260
- if model in self.model_instructions:
261
- return Prompt(text=self.model_instructions[model])
262
- else:
263
- if hasattr(self, "new_default_instructions"):
264
- return self.new_default_instructions
265
- else:
266
- return self.applicable_prompts(model)[0]()
edsl/questions/Quick.py DELETED
@@ -1,41 +0,0 @@
1
- from edsl import (
2
- QuestionFreeText,
3
- QuestionMultipleChoice,
4
- Survey,
5
- QuestionList,
6
- Question,
7
- )
8
-
9
-
10
- def Quick(question_text):
11
- q_type = QuestionMultipleChoice(
12
- question_text=f"A researcher is asking a language model this: {question_text}. What is the most appropriate type of question to ask?",
13
- question_name="potential_question_type",
14
- question_options=["multiple_choice", "list", "free_text"],
15
- )
16
-
17
- q_name = QuestionFreeText(
18
- question_text=f"A researcher is asking a language model this: {question_text}. What is a good name for this question that's a valid python identifier? Just return the proposed identifer",
19
- question_name="potential_question_name",
20
- )
21
-
22
- q_options = QuestionList(
23
- question_text=f"A research is asking this question: { question_text }. What are the possible options for this question?",
24
- question_name="potential_question_options",
25
- )
26
-
27
- survey = Survey([q_type, q_name, q_options]).add_skip_rule(
28
- q_options, "{{ potential_question_type }} != 'multiple_choice'"
29
- )
30
- return survey
31
- # results = survey.run()
32
- # question_type = results.select("potential_question_type").first()
33
- # question_options = results.select("potential_question_options").first()
34
- # question_name = results.select("potential_question_name").first()
35
- # print("Question Type: ", question_type)
36
- # print("Question Name: ", question_name)
37
- # print("Question Options: ", question_options)
38
- # if question_options == None:
39
- # return Question(question_type, question_name = question_name)
40
- # else:
41
- # return Question(question_type, question_name = question_name, question_options = question_options)