hamtaa-texttools 1.0.4__py3-none-any.whl → 1.0.5__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.

Potentially problematic release.


This version of hamtaa-texttools might be problematic. Click here for more details.

Files changed (32) hide show
  1. {hamtaa_texttools-1.0.4.dist-info → hamtaa_texttools-1.0.5.dist-info}/METADATA +192 -141
  2. hamtaa_texttools-1.0.5.dist-info/RECORD +30 -0
  3. {hamtaa_texttools-1.0.4.dist-info → hamtaa_texttools-1.0.5.dist-info}/licenses/LICENSE +20 -20
  4. {hamtaa_texttools-1.0.4.dist-info → hamtaa_texttools-1.0.5.dist-info}/top_level.txt +0 -0
  5. texttools/__init__.py +9 -9
  6. texttools/batch/__init__.py +4 -4
  7. texttools/batch/batch_manager.py +240 -240
  8. texttools/batch/batch_runner.py +212 -212
  9. texttools/formatters/base_formatter.py +33 -33
  10. texttools/formatters/{user_merge_formatter/user_merge_formatter.py → user_merge_formatter.py} +30 -30
  11. texttools/prompts/README.md +31 -31
  12. texttools/prompts/categorizer.yaml +28 -31
  13. texttools/prompts/custom_tool.yaml +7 -0
  14. texttools/prompts/keyword_extractor.yaml +18 -14
  15. texttools/prompts/ner_extractor.yaml +20 -21
  16. texttools/prompts/question_detector.yaml +13 -14
  17. texttools/prompts/question_generator.yaml +19 -22
  18. texttools/prompts/question_merger.yaml +45 -48
  19. texttools/prompts/rewriter.yaml +111 -0
  20. texttools/prompts/subject_question_generator.yaml +22 -26
  21. texttools/prompts/summarizer.yaml +13 -11
  22. texttools/prompts/translator.yaml +14 -14
  23. texttools/tools/__init__.py +4 -4
  24. texttools/tools/async_the_tool.py +277 -263
  25. texttools/tools/internals/async_operator.py +297 -288
  26. texttools/tools/internals/operator.py +295 -306
  27. texttools/tools/internals/output_models.py +52 -62
  28. texttools/tools/internals/prompt_loader.py +76 -82
  29. texttools/tools/the_tool.py +501 -400
  30. hamtaa_texttools-1.0.4.dist-info/RECORD +0 -29
  31. texttools/prompts/question_rewriter.yaml +0 -46
  32. {hamtaa_texttools-1.0.4.dist-info → hamtaa_texttools-1.0.5.dist-info}/WHEEL +0 -0
@@ -1,400 +1,501 @@
1
- from typing import Literal, Any, Optional
2
-
3
- from openai import OpenAI
4
-
5
- from texttools.tools.internals.operator import Operator
6
- import texttools.tools.internals.output_models as OutputModels
7
-
8
-
9
- class TheTool:
10
- """
11
- High-level interface exposing specialized text tools for.
12
-
13
- Each method configures the operator with a specific YAML prompt,
14
- output schema, and flags, then delegates execution to `operator.run()`.
15
-
16
- Supported capabilities:
17
- - categorize: assign a text to one of several Islamic categories.
18
- - extract_keywords: produce a keyword list from text.
19
- - extract_entities: simple NER (name/type pairs).
20
- - detect_question: binary check whether input is a question.
21
- - generate_question_from_text: produce a new question from a text.
22
- - merge_questions: combine multiple questions (default/reason modes).
23
- - rewrite_question: rephrase questions (same meaning/different wording, or vice versa).
24
- - generate_questions_from_subject: generate multiple questions given a subject.
25
- - summarize: produce a concise summary of a subject.
26
- - translate: translate text between languages.
27
-
28
- Usage pattern:
29
- client = OpenAI(...)
30
- tool = TheTool(client, model="gemma-3")
31
- result = tool.categorize("متن ورودی ...", with_analysis=True)
32
- """
33
-
34
- def __init__(
35
- self,
36
- client: OpenAI,
37
- *,
38
- model: str,
39
- temperature: float = 0.0,
40
- **client_kwargs: Any,
41
- ):
42
- self.operator = Operator(
43
- client=client,
44
- model=model,
45
- temperature=temperature,
46
- **client_kwargs,
47
- )
48
-
49
- def categorize(
50
- self,
51
- text: str,
52
- with_analysis: bool = False,
53
- user_prompt: str = "",
54
- logprobs: bool = False,
55
- top_logprobs: int = 8,
56
- ) -> dict[str, str]:
57
- """
58
- Categorize a text into a single Islamic studies domain category.
59
-
60
- Args:
61
- text: Input string to categorize.
62
- with_analysis: If True, first runs an LLM "analysis" step and
63
- conditions the main prompt on that analysis.
64
-
65
- Returns:
66
- {"result": <category string>}
67
- Example: {"result": "باورهای دینی"}
68
- """
69
-
70
- results = self.operator.run(
71
- text,
72
- prompt_file="categorizer.yaml",
73
- output_model=OutputModels.CategorizerOutput,
74
- with_analysis=with_analysis,
75
- resp_format="parse",
76
- user_prompt=user_prompt,
77
- logprobs=logprobs,
78
- top_logprobs=top_logprobs,
79
- )
80
-
81
- return results
82
-
83
- def extract_keywords(
84
- self,
85
- text: str,
86
- output_lang: Optional[str] = None,
87
- with_analysis: bool = False,
88
- user_prompt: str = "",
89
- logprobs: bool = False,
90
- top_logprobs: int = 3,
91
- ) -> dict[str, list[str]]:
92
- """
93
- Extract salient keywords from text.
94
-
95
- Args:
96
- text: Input string to analyze.
97
- with_analysis: Whether to run an extra LLM reasoning step.
98
-
99
- Returns:
100
- {"result": [<keyword1>, <keyword2>, ...]}
101
- """
102
- results = self.operator.run(
103
- text,
104
- prompt_file="keyword_extractor.yaml",
105
- output_model=OutputModels.ListStrOutput,
106
- with_analysis=with_analysis,
107
- resp_format="parse",
108
- user_prompt=user_prompt,
109
- output_lang=output_lang,
110
- logprobs=logprobs,
111
- top_logprobs=top_logprobs,
112
- )
113
-
114
- return results
115
-
116
- def extract_entities(
117
- self,
118
- text: str,
119
- output_lang: Optional[str] = None,
120
- with_analysis: bool = False,
121
- user_prompt: str = "",
122
- logprobs: bool = False,
123
- top_logprobs: int = 3,
124
- ) -> dict[str, list[dict[str, str]]]:
125
- """
126
- Perform Named Entity Recognition (NER) over the input text.
127
-
128
- Args:
129
- text: Input string.
130
- with_analysis: Whether to run an extra LLM reasoning step.
131
-
132
- Returns:
133
- {"result": [{"text": <entity>, "type": <entity_type>}, ...]}
134
- """
135
- results = self.operator.run(
136
- text,
137
- prompt_file="ner_extractor.yaml",
138
- output_model=OutputModels.ListDictStrStrOutput,
139
- with_analysis=with_analysis,
140
- resp_format="parse",
141
- user_prompt=user_prompt,
142
- output_lang=output_lang,
143
- logprobs=logprobs,
144
- top_logprobs=top_logprobs,
145
- )
146
-
147
- return results
148
-
149
- def detect_question(
150
- self,
151
- question: str,
152
- output_lang: Optional[str] = None,
153
- with_analysis: bool = False,
154
- user_prompt: str = "",
155
- logprobs: bool = False,
156
- top_logprobs: int = 2,
157
- ) -> dict[str, bool]:
158
- """
159
- Detect if the input is phrased as a question.
160
-
161
- Args:
162
- question: Input string to evaluate.
163
- with_analysis: Whether to include an analysis step.
164
-
165
- Returns:
166
- {"result": "true"} or {"result": "false"}
167
- """
168
- results = self.operator.run(
169
- question,
170
- prompt_file="question_detector.yaml",
171
- output_model=OutputModels.BoolOutput,
172
- with_analysis=with_analysis,
173
- resp_format="parse",
174
- user_prompt=user_prompt,
175
- output_lang=output_lang,
176
- logprobs=logprobs,
177
- top_logprobs=top_logprobs,
178
- )
179
-
180
- return results
181
-
182
- def generate_question_from_text(
183
- self,
184
- text: str,
185
- output_lang: Optional[str] = None,
186
- with_analysis: bool = False,
187
- user_prompt: str = "",
188
- logprobs: bool = False,
189
- top_logprobs: int = 3,
190
- ) -> dict[str, str]:
191
- """
192
- Generate a single question from the given text.
193
-
194
- Args:
195
- text: Source text to derive a question from.
196
- with_analysis: Whether to use analysis before generation.
197
-
198
- Returns:
199
- {"result": <generated_question>}
200
- """
201
- results = self.operator.run(
202
- text,
203
- prompt_file="question_generator.yaml",
204
- output_model=OutputModels.StrOutput,
205
- with_analysis=with_analysis,
206
- resp_format="parse",
207
- user_prompt=user_prompt,
208
- output_lang=output_lang,
209
- logprobs=logprobs,
210
- top_logprobs=top_logprobs,
211
- )
212
-
213
- return results
214
-
215
- def merge_questions(
216
- self,
217
- questions: list[str],
218
- output_lang: Optional[str] = None,
219
- mode: Literal["default", "reason"] = "default",
220
- with_analysis: bool = False,
221
- user_prompt: str = "",
222
- logprobs: bool = False,
223
- top_logprobs: int = 3,
224
- ) -> dict[str, str]:
225
- """
226
- Merge multiple questions into a single unified question.
227
-
228
- Args:
229
- questions: List of question strings.
230
- mode: Merge strategy:
231
- - "default": simple merging.
232
- - "reason": merging with reasoning explanation.
233
- with_analysis: Whether to use an analysis step.
234
-
235
- Returns:
236
- {"result": <merged_question>}
237
- """
238
- question_str = ", ".join(questions)
239
-
240
- results = self.operator.run(
241
- question_str,
242
- prompt_file="question_merger.yaml",
243
- output_model=OutputModels.StrOutput,
244
- with_analysis=with_analysis,
245
- use_modes=True,
246
- mode=mode,
247
- resp_format="parse",
248
- user_prompt=user_prompt,
249
- output_lang=output_lang,
250
- logprobs=logprobs,
251
- top_logprobs=top_logprobs,
252
- )
253
-
254
- return results
255
-
256
- def rewrite_question(
257
- self,
258
- question: str,
259
- output_lang: Optional[str] = None,
260
- mode: Literal[
261
- "same_meaning_different_wording",
262
- "different_meaning_similar_wording",
263
- ] = "same_meaning_different_wording",
264
- with_analysis: bool = False,
265
- user_prompt: str = "",
266
- logprobs: bool = False,
267
- top_logprobs: int = 3,
268
- ) -> dict[str, str]:
269
- """
270
- Rewrite a question with different wording or meaning.
271
-
272
- Args:
273
- question: Input question to rewrite.
274
- mode: Rewrite strategy:
275
- - "same_meaning_different_wording": keep meaning, change words.
276
- - "different_meaning_similar_wording": alter meaning, preserve wording style.
277
- with_analysis: Whether to include an analysis step.
278
-
279
- Returns:
280
- {"result": <rewritten_question>}
281
- """
282
- results = self.operator.run(
283
- question,
284
- prompt_file="question_rewriter.yaml",
285
- output_model=OutputModels.StrOutput,
286
- with_analysis=with_analysis,
287
- use_modes=True,
288
- mode=mode,
289
- resp_format="parse",
290
- user_prompt=user_prompt,
291
- output_lang=output_lang,
292
- logprobs=logprobs,
293
- top_logprobs=top_logprobs,
294
- )
295
-
296
- return results
297
-
298
- def generate_questions_from_subject(
299
- self,
300
- subject: str,
301
- number_of_questions: int,
302
- output_lang: Optional[str] = None,
303
- with_analysis: bool = False,
304
- user_prompt: str = "",
305
- logprobs: bool = False,
306
- top_logprobs: int = 3,
307
- ) -> dict[str, list[str]]:
308
- """
309
- Generate a list of questions about a subject.
310
-
311
- Args:
312
- subject: Topic of interest.
313
- number_of_questions: Number of questions to produce.
314
- language: Target language for generated questions.
315
- with_analysis: Whether to include an analysis step.
316
-
317
- Returns:
318
- {"result": [<question1>, <question2>, ...]}
319
- """
320
- results = self.operator.run(
321
- subject,
322
- prompt_file="subject_question_generator.yaml",
323
- output_model=OutputModels.ReasonListStrOutput,
324
- with_analysis=with_analysis,
325
- resp_format="parse",
326
- user_prompt=user_prompt,
327
- number_of_questions=number_of_questions,
328
- output_lang=output_lang,
329
- logprobs=logprobs,
330
- top_logprobs=top_logprobs,
331
- )
332
-
333
- return results
334
-
335
- def summarize(
336
- self,
337
- text: str,
338
- output_lang: Optional[str] = None,
339
- with_analysis: bool = False,
340
- user_prompt: str = "",
341
- logprobs: bool = False,
342
- top_logprobs: int = 3,
343
- ) -> dict[str, str]:
344
- """
345
- Summarize the given subject text.
346
-
347
- Args:
348
- subject: Input text to summarize.
349
- with_analysis: Whether to include an analysis step.
350
-
351
- Returns:
352
- {"result": <summary>}
353
- """
354
- results = self.operator.run(
355
- text,
356
- prompt_file="summarizer.yaml",
357
- output_model=OutputModels.StrOutput,
358
- with_analysis=with_analysis,
359
- resp_format="parse",
360
- user_prompt=user_prompt,
361
- output_lang=output_lang,
362
- logprobs=logprobs,
363
- top_logprobs=top_logprobs,
364
- )
365
-
366
- return results
367
-
368
- def translate(
369
- self,
370
- text: str,
371
- target_language: str,
372
- with_analysis: bool = False,
373
- user_prompt: str = "",
374
- logprobs: bool = False,
375
- top_logprobs: int = 3,
376
- ) -> dict[str, str]:
377
- """
378
- Translate text between languages.
379
-
380
- Args:
381
- text: Input string to translate.
382
- target_language: Language code or name to translate into.
383
- with_analysis: Whether to include an analysis step.
384
-
385
- Returns:
386
- {"result": <translated_text>}
387
- """
388
- results = self.operator.run(
389
- text,
390
- prompt_file="translator.yaml",
391
- output_model=OutputModels.StrOutput,
392
- with_analysis=with_analysis,
393
- resp_format="parse",
394
- user_prompt=user_prompt,
395
- target_language=target_language,
396
- logprobs=logprobs,
397
- top_logprobs=top_logprobs,
398
- )
399
-
400
- return results
1
+ from typing import Literal, Any
2
+
3
+ from openai import OpenAI
4
+
5
+ from texttools.tools.internals.operator import Operator
6
+ import texttools.tools.internals.output_models as OutputModels
7
+
8
+
9
+ class TheTool:
10
+ """
11
+ High-level interface exposing specialized text tools for.
12
+
13
+ Each method configures the operator with a specific YAML prompt,
14
+ output schema, and flags, then delegates execution to `operator.run()`.
15
+
16
+ Supported capabilities:
17
+ - categorize: assign a text to one of several Islamic categories.
18
+ - extract_keywords: produce a keyword list from text.
19
+ - extract_entities: simple NER (name/type pairs).
20
+ - detect_question: binary check whether input is a question.
21
+ - generate_question_from_text: produce a new question from a text.
22
+ - merge_questions: combine multiple questions (default/reason modes).
23
+ - rewrite: rephrase questions (same meaning/different wording, or vice versa).
24
+ - generate_questions_from_subject: generate multiple questions given a subject.
25
+ - summarize: produce a concise summary of a subject.
26
+ - translate: translate text between languages.
27
+
28
+ Usage pattern:
29
+ client = OpenAI(...)
30
+ tool = TheTool(client, model="gemma-3")
31
+ result = tool.categorize("متن ورودی ...", with_analysis=True)
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ client: OpenAI,
37
+ *,
38
+ model: str = "google/gemma-3n-e4b-it",
39
+ user_prompt: str | None = None,
40
+ output_lang: str | None = None,
41
+ with_analysis: bool = False,
42
+ temperature: float = 0.0,
43
+ logprobs: bool = False,
44
+ top_logprobs: int = 3,
45
+ ):
46
+ # Initialize Operator
47
+ self.operator = Operator(client=client)
48
+
49
+ # Initialize default values
50
+ self.model = model
51
+ self.user_prompt = user_prompt
52
+ self.output_lang = output_lang
53
+ self.with_analysis = with_analysis
54
+ self.temperature = temperature
55
+ self.logprobs = logprobs
56
+ self.top_logprobs = top_logprobs
57
+
58
+ def categorize(
59
+ self,
60
+ text: str,
61
+ model: str | None = None,
62
+ user_prompt: str | None = None,
63
+ output_lang: str | None = None,
64
+ with_analysis: bool | None = None,
65
+ temperature: float | None = None,
66
+ logprobs: bool | None = None,
67
+ top_logprobs: int | None = None,
68
+ ) -> dict[str, str]:
69
+ """
70
+ Categorize a text into a single Islamic studies domain category.
71
+
72
+ Args:
73
+ text: Input string to categorize.
74
+ with_analysis: If True, first runs an LLM "analysis" step and
75
+ conditions the main prompt on that analysis.
76
+
77
+ Returns:
78
+ {"result": <category string>}
79
+ Example: {"result": "باورهای دینی"}
80
+ """
81
+ return self.operator.run(
82
+ # Internal parameters
83
+ prompt_file="categorizer.yaml",
84
+ output_model=OutputModels.CategorizerOutput,
85
+ resp_format="parse",
86
+ # User parameters
87
+ text=text,
88
+ model=self.model if model is None else model,
89
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
90
+ output_lang=self.output_lang if output_lang is None else output_lang,
91
+ with_analysis=self.with_analysis
92
+ if with_analysis is None
93
+ else with_analysis,
94
+ temperature=self.temperature if temperature is None else temperature,
95
+ logprobs=self.logprobs if logprobs is None else logprobs,
96
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
97
+ )
98
+
99
+ def extract_keywords(
100
+ self,
101
+ text: str,
102
+ model: str | None = None,
103
+ user_prompt: str | None = None,
104
+ output_lang: str | None = None,
105
+ with_analysis: bool | None = None,
106
+ temperature: float | None = None,
107
+ logprobs: bool | None = None,
108
+ top_logprobs: int | None = None,
109
+ ) -> dict[str, list[str]]:
110
+ """
111
+ Extract salient keywords from text.
112
+
113
+ Args:
114
+ text: Input string to analyze.
115
+ with_analysis: Whether to run an extra LLM reasoning step.
116
+
117
+ Returns:
118
+ {"result": [<keyword1>, <keyword2>, ...]}
119
+ """
120
+ return self.operator.run(
121
+ # Internal parameters
122
+ prompt_file="keyword_extractor.yaml",
123
+ output_model=OutputModels.ListStrOutput,
124
+ resp_format="parse",
125
+ # User parameters
126
+ text=text,
127
+ model=self.model if model is None else model,
128
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
129
+ output_lang=self.output_lang if output_lang is None else output_lang,
130
+ with_analysis=self.with_analysis
131
+ if with_analysis is None
132
+ else with_analysis,
133
+ temperature=self.temperature if temperature is None else temperature,
134
+ logprobs=self.logprobs if logprobs is None else logprobs,
135
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
136
+ )
137
+
138
+ def extract_entities(
139
+ self,
140
+ text: str,
141
+ model: str | None = None,
142
+ user_prompt: str | None = None,
143
+ output_lang: str | None = None,
144
+ with_analysis: bool | None = None,
145
+ temperature: float | None = None,
146
+ logprobs: bool | None = None,
147
+ top_logprobs: int | None = None,
148
+ ) -> dict[str, list[dict[str, str]]]:
149
+ """
150
+ Perform Named Entity Recognition (NER) over the input text.
151
+
152
+ Args:
153
+ text: Input string.
154
+ with_analysis: Whether to run an extra LLM reasoning step.
155
+
156
+ Returns:
157
+ {"result": [{"text": <entity>, "type": <entity_type>}, ...]}
158
+ """
159
+ return self.operator.run(
160
+ # Internal parameters
161
+ prompt_file="ner_extractor.yaml",
162
+ output_model=OutputModels.ListDictStrStrOutput,
163
+ resp_format="parse",
164
+ # User parameters
165
+ text=text,
166
+ model=self.model if model is None else model,
167
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
168
+ output_lang=self.output_lang if output_lang is None else output_lang,
169
+ with_analysis=self.with_analysis
170
+ if with_analysis is None
171
+ else with_analysis,
172
+ temperature=self.temperature if temperature is None else temperature,
173
+ logprobs=self.logprobs if logprobs is None else logprobs,
174
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
175
+ )
176
+
177
+ def detect_question(
178
+ self,
179
+ text: str,
180
+ model: str | None = None,
181
+ user_prompt: str | None = None,
182
+ with_analysis: bool | None = None,
183
+ temperature: float | None = None,
184
+ logprobs: bool | None = None,
185
+ top_logprobs: int | None = None,
186
+ ) -> dict[str, bool]:
187
+ """
188
+ Detect if the input is phrased as a question.
189
+
190
+ Args:
191
+ question: Input string to evaluate.
192
+ with_analysis: Whether to include an analysis step.
193
+
194
+ Returns:
195
+ {"result": "true"} or {"result": "false"}
196
+ """
197
+ return self.operator.run(
198
+ # Internal parameters
199
+ prompt_file="question_detector.yaml",
200
+ output_model=OutputModels.BoolOutput,
201
+ resp_format="parse",
202
+ output_lang=False,
203
+ # User parameters
204
+ text=text,
205
+ model=self.model if model is None else model,
206
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
207
+ with_analysis=self.with_analysis
208
+ if with_analysis is None
209
+ else with_analysis,
210
+ temperature=self.temperature if temperature is None else temperature,
211
+ logprobs=self.logprobs if logprobs is None else logprobs,
212
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
213
+ )
214
+
215
+ def generate_question_from_text(
216
+ self,
217
+ text: str,
218
+ model: str | None = None,
219
+ user_prompt: str | None = None,
220
+ output_lang: str | None = None,
221
+ with_analysis: bool | None = None,
222
+ temperature: float | None = None,
223
+ logprobs: bool | None = None,
224
+ top_logprobs: int | None = None,
225
+ ) -> dict[str, str]:
226
+ """
227
+ Generate a single question from the given text.
228
+
229
+ Args:
230
+ text: Source text to derive a question from.
231
+ with_analysis: Whether to use analysis before generation.
232
+
233
+ Returns:
234
+ {"result": <generated_question>}
235
+ """
236
+ return self.operator.run(
237
+ # Internal parameters
238
+ prompt_file="question_generator.yaml",
239
+ output_model=OutputModels.StrOutput,
240
+ resp_format="parse",
241
+ # User parameters
242
+ text=text,
243
+ model=self.model if model is None else model,
244
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
245
+ output_lang=self.output_lang if output_lang is None else output_lang,
246
+ with_analysis=self.with_analysis
247
+ if with_analysis is None
248
+ else with_analysis,
249
+ temperature=self.temperature if temperature is None else temperature,
250
+ logprobs=self.logprobs if logprobs is None else logprobs,
251
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
252
+ )
253
+
254
+ def merge_questions(
255
+ self,
256
+ questions: list[str],
257
+ mode: Literal["default", "reason"] = "default",
258
+ model: str | None = None,
259
+ user_prompt: str | None = None,
260
+ output_lang: str | None = None,
261
+ with_analysis: bool | None = None,
262
+ temperature: float | None = None,
263
+ logprobs: bool | None = None,
264
+ top_logprobs: int | None = None,
265
+ ) -> dict[str, str]:
266
+ """
267
+ Merge multiple questions into a single unified question.
268
+
269
+ Args:
270
+ questions: List of question strings.
271
+ mode: Merge strategy:
272
+ - "default": simple merging.
273
+ - "reason": merging with reasoning explanation.
274
+ with_analysis: Whether to use an analysis step.
275
+
276
+ Returns:
277
+ {"result": <merged_question>}
278
+ """
279
+ text = ", ".join(questions)
280
+ return self.operator.run(
281
+ # Internal parameters
282
+ prompt_file="question_merger.yaml",
283
+ output_model=OutputModels.StrOutput,
284
+ resp_format="parse",
285
+ # User parameters
286
+ text=text,
287
+ mode=mode,
288
+ model=self.model if model is None else model,
289
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
290
+ output_lang=self.output_lang if output_lang is None else output_lang,
291
+ with_analysis=self.with_analysis
292
+ if with_analysis is None
293
+ else with_analysis,
294
+ temperature=self.temperature if temperature is None else temperature,
295
+ logprobs=self.logprobs if logprobs is None else logprobs,
296
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
297
+ )
298
+
299
+ def rewrite(
300
+ self,
301
+ text: str,
302
+ mode: Literal["positive", "negative", "hard_negative"] = "positive",
303
+ model: str | None = None,
304
+ user_prompt: str | None = None,
305
+ output_lang: str | None = None,
306
+ with_analysis: bool | None = None,
307
+ temperature: float | None = None,
308
+ logprobs: bool | None = None,
309
+ top_logprobs: int | None = None,
310
+ ) -> dict[str, str]:
311
+ """
312
+ Rewrite a question with different wording or meaning.
313
+
314
+ Args:
315
+ question: Input question to rewrite.
316
+ mode: Rewrite strategy:
317
+ - "positive": keep meaning, change words.
318
+ - "negative": alter meaning, preserve wording style.
319
+ with_analysis: Whether to include an analysis step.
320
+
321
+ Returns:
322
+ {"result": <rewritten_question>}
323
+ """
324
+ return self.operator.run(
325
+ # Internal parameters
326
+ prompt_file="rewriter.yaml",
327
+ output_model=OutputModels.StrOutput,
328
+ resp_format="parse",
329
+ # User parameters
330
+ text=text,
331
+ mode=mode,
332
+ model=self.model if model is None else model,
333
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
334
+ output_lang=self.output_lang if output_lang is None else output_lang,
335
+ with_analysis=self.with_analysis
336
+ if with_analysis is None
337
+ else with_analysis,
338
+ temperature=self.temperature if temperature is None else temperature,
339
+ logprobs=self.logprobs if logprobs is None else logprobs,
340
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
341
+ )
342
+
343
+ def generate_questions_from_subject(
344
+ self,
345
+ text: str,
346
+ number_of_questions: int,
347
+ model: str | None = None,
348
+ user_prompt: str | None = None,
349
+ output_lang: str | None = None,
350
+ with_analysis: bool | None = None,
351
+ temperature: float | None = None,
352
+ logprobs: bool | None = None,
353
+ top_logprobs: int | None = None,
354
+ ) -> dict[str, list[str]]:
355
+ """
356
+ Generate a list of questions about a subject.
357
+
358
+ Args:
359
+ subject: Topic of interest.
360
+ number_of_questions: Number of questions to produce.
361
+ language: Target language for generated questions.
362
+ with_analysis: Whether to include an analysis step.
363
+
364
+ Returns:
365
+ {"result": [<question1>, <question2>, ...]}
366
+ """
367
+ return self.operator.run(
368
+ # Internal parameters
369
+ prompt_file="subject_question_generator.yaml",
370
+ output_model=OutputModels.ReasonListStrOutput,
371
+ resp_format="parse",
372
+ # User parameters
373
+ text=text,
374
+ number_of_questions=number_of_questions,
375
+ model=self.model if model is None else model,
376
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
377
+ output_lang=self.output_lang if output_lang is None else output_lang,
378
+ with_analysis=self.with_analysis
379
+ if with_analysis is None
380
+ else with_analysis,
381
+ temperature=self.temperature if temperature is None else temperature,
382
+ logprobs=self.logprobs if logprobs is None else logprobs,
383
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
384
+ )
385
+
386
+ def summarize(
387
+ self,
388
+ text: str,
389
+ model: str | None = None,
390
+ user_prompt: str | None = None,
391
+ output_lang: str | None = None,
392
+ with_analysis: bool | None = None,
393
+ temperature: float | None = None,
394
+ logprobs: bool | None = None,
395
+ top_logprobs: int | None = None,
396
+ ) -> dict[str, str]:
397
+ """
398
+ Summarize the given subject text.
399
+
400
+ Args:
401
+ subject: Input text to summarize.
402
+ with_analysis: Whether to include an analysis step.
403
+
404
+ Returns:
405
+ {"result": <summary>}
406
+ """
407
+ return self.operator.run(
408
+ # Internal parameters
409
+ prompt_file="summarizer.yaml",
410
+ output_model=OutputModels.StrOutput,
411
+ resp_format="parse",
412
+ # User paramaeters
413
+ text=text,
414
+ model=self.model if model is None else model,
415
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
416
+ output_lang=self.output_lang if output_lang is None else output_lang,
417
+ with_analysis=self.with_analysis
418
+ if with_analysis is None
419
+ else with_analysis,
420
+ temperature=self.temperature if temperature is None else temperature,
421
+ logprobs=self.logprobs if logprobs is None else logprobs,
422
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
423
+ )
424
+
425
+ def translate(
426
+ self,
427
+ text: str,
428
+ target_language: str,
429
+ model: str | None = None,
430
+ user_prompt: str | None = None,
431
+ with_analysis: bool | None = None,
432
+ temperature: float | None = None,
433
+ logprobs: bool | None = None,
434
+ top_logprobs: int | None = None,
435
+ ) -> dict[str, str]:
436
+ """
437
+ Translate text between languages.
438
+
439
+ Args:
440
+ text: Input string to translate.
441
+ target_language: Language code or name to translate into.
442
+ with_analysis: Whether to include an analysis step.
443
+
444
+ Returns:
445
+ {"result": <translated_text>}
446
+ """
447
+ return self.operator.run(
448
+ # Internal parameters
449
+ prompt_file="translator.yaml",
450
+ output_model=OutputModels.StrOutput,
451
+ resp_format="parse",
452
+ output_lang=False,
453
+ # User parameters
454
+ text=text,
455
+ target_language=target_language,
456
+ model=self.model if model is None else model,
457
+ user_prompt=self.user_prompt if user_prompt is None else user_prompt,
458
+ with_analysis=self.with_analysis
459
+ if with_analysis is None
460
+ else with_analysis,
461
+ temperature=self.temperature if temperature is None else temperature,
462
+ logprobs=self.logprobs if logprobs is None else logprobs,
463
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
464
+ )
465
+
466
+ def custom_tool(
467
+ self,
468
+ prompt: str,
469
+ output_model: Any,
470
+ model: str | None = None,
471
+ output_lang: str | None = None,
472
+ temperature: float | None = None,
473
+ logprobs: float | None = None,
474
+ top_logprobs: int | None = None,
475
+ ) -> dict[str, Any]:
476
+ """
477
+ Custom tool that can do almost anything!
478
+
479
+ Args:
480
+ prompt: Custom prompt.
481
+ output_model: Custom BaseModel output model.
482
+
483
+ Returns:
484
+ {"result": <Any>}
485
+ """
486
+ return self.operator.run(
487
+ # Internal parameters
488
+ prompt_file="custom_tool.yaml",
489
+ resp_format="parse",
490
+ user_prompt=False,
491
+ with_analysis=False,
492
+ # User paramaeters
493
+ text=prompt,
494
+ output_model=output_model,
495
+ output_model_str=output_model.model_json_schema(),
496
+ model=self.model if model is None else model,
497
+ output_lang=self.output_lang if output_lang is None else output_lang,
498
+ temperature=self.temperature if temperature is None else temperature,
499
+ logprobs=self.logprobs if logprobs is None else logprobs,
500
+ top_logprobs=self.top_logprobs if top_logprobs is None else top_logprobs,
501
+ )