pop-python 1.0.0__py3-none-any.whl → 1.0.3__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.
POP/POP.py ADDED
@@ -0,0 +1,392 @@
1
+ import re
2
+ import json
3
+ import requests
4
+ from dotenv import load_dotenv
5
+ from os import getenv, path
6
+ from .LLMClient import LLMClient, OpenAIClient, GeminiClient, DeepseekClient, LocalPyTorchClient, DoubaoClient, OllamaClient
7
+
8
+ # Load environment variables
9
+ load_dotenv()
10
+ default_model = {
11
+ "OpenAIClient": "gpt-5-nano",
12
+ "GeminiClient": "gemini-2.5-flash",
13
+ "DeepseekClient": "deepseek-chat",
14
+ "DoubaoClient": "doubao-seed-1-6-flash-250715",
15
+ "OllamaClient": "mistral:7b",
16
+ }
17
+ client_map = {
18
+ "openai": OpenAIClient,
19
+ "gemini": GeminiClient,
20
+ "local": LocalPyTorchClient,
21
+ "deepseek": DeepseekClient,
22
+ "doubao": DoubaoClient,
23
+ "ollama": OllamaClient
24
+ }
25
+
26
+ ##############################################
27
+ # PromptFunction Class
28
+ ##############################################
29
+
30
+ class PromptFunction:
31
+ """
32
+ A class representing a reusable prompt function.
33
+ """
34
+ def __init__(self,
35
+ sys_prompt: str = "",
36
+ prompt: str = "",
37
+ client: LLMClient | str = None):
38
+ """
39
+ Initializes a new prompt function.
40
+
41
+ Args:
42
+ prompt (str): The base prompt template.
43
+ sys_prompt (str): The system prompt for additional context.
44
+ client (LLMClient | str): An instance of an LLM client or a string identifier. Defaults to OpenAIClient. ("openai", "gemini", "local", "deepseek")
45
+ """
46
+ self.prompt = prompt
47
+ self.sys_prompt = sys_prompt
48
+ self.placeholders = self._get_place_holder()
49
+ self.client = None
50
+
51
+ if isinstance(client, LLMClient):
52
+ self.client = client
53
+ elif isinstance(client, str):
54
+ self.client = client_map.get(client, None)
55
+ if self.client:
56
+ self.client = self.client() # instantiate LLMClient if user passed a string
57
+
58
+ # gpt-5/mini/nano only supports temperature 1
59
+ self.temperature = 1 if self.client.__class__.__name__ == "OpenAIClient" and default_model[self.client.__class__.__name__] in ["gpt-5-nano", "gpt-5-mini", "gpt-5"] else 0.0
60
+ self.last_response = None
61
+ self.default_model_name = default_model[self.client.__class__.__name__]
62
+ print(f"[PromptFunction] Using client: {self.client.__class__.__name__}, default model: {self.default_model_name}")
63
+
64
+ def execute(self, *args, **kwargs) -> str:
65
+ """
66
+ Executes the prompt function with dynamic argument injection.
67
+
68
+ Special keys in kwargs:
69
+ - model: Model name (defaults to self.default_model_name).
70
+ - sys: Additional system instructions.
71
+ - fmt: Response format/schema.
72
+ - tools: List of function tools to use (for function calling).
73
+ - temp: Temperature.
74
+ - ADD_BEFORE: Text to prepend.
75
+ - ADD_AFTER: Text to append.
76
+
77
+ Args:
78
+ *args: Positional arguments to add to the prompt.
79
+ **kwargs: Keyword arguments for placeholder replacement or extra context.
80
+
81
+ Returns:
82
+ str: The LLM-generated response.
83
+ """
84
+ model = kwargs.pop("model", self.default_model_name)
85
+ system_extra = kwargs.pop("sys", "")
86
+ fmt = kwargs.pop("fmt", None)
87
+ tools = kwargs.pop("tools", None)
88
+ temp = kwargs.pop("temp", self.temperature)
89
+ images = kwargs.pop("images", None)
90
+
91
+ # Prepare the prompt with dynamic injections.
92
+ formatted_prompt = self._prepare_prompt(*args, **kwargs)
93
+
94
+ # Build the message payload.
95
+ system_message = {
96
+ "role": "system",
97
+ "content": (
98
+ f"You are a general-purpose helpful assistant that is responsible for executing Prompt Functions, you will receive instructions and return as ordered. "
99
+ f"Since your return is expected to be read by code most of the time, DO NOT wrap your returns in '```' tags unless user explicitly asks for markdown or similar format. "
100
+ f"Base system prompt:\n{self.sys_prompt}\n\n"
101
+ f"Additional instructions:\n{system_extra}"
102
+ )
103
+ }
104
+ user_message = {"role": "user", "content": f"<if no user message, check system prompt.> {formatted_prompt}"}
105
+ messages = [system_message, user_message]
106
+
107
+ try:
108
+ # Call the LLM client.
109
+ raw_response = self.client.chat_completion(
110
+ messages=messages,
111
+ model=model,
112
+ temperature=temp,
113
+ response_format=fmt,
114
+ tools=tools,
115
+ images=images
116
+ )
117
+ except Exception as e:
118
+ verbose = True
119
+ if verbose:
120
+ print(f"Error occurred while executing prompt function: {e}\nparameters:\nmodel: {model}\ntemperature: {temp}\nprompt: {formatted_prompt}\nsys: {system_extra}\nformat: {fmt}\ntools: {tools}\nimages: {images}")
121
+ # raise RuntimeError(f"Error occurred while executing prompt function: {e}\n")
122
+ print(f"Error occurred while executing prompt function: {e}\n")
123
+ return ""
124
+
125
+ # Save entire response
126
+ self.last_response = raw_response
127
+
128
+ # default to message content
129
+ reply_content = raw_response.choices[0].message.content
130
+
131
+ # if it is a function call, extract the arguments
132
+ if raw_response.choices[0].message.tool_calls:
133
+ tool_call = raw_response.choices[0].message.tool_calls[0]
134
+ reply_content = tool_call.function.arguments
135
+
136
+ return reply_content
137
+
138
+ def _prepare_prompt(self, *args, **kwargs) -> str:
139
+ """
140
+ Prepares the prompt by injecting dynamic arguments into placeholders.
141
+ Special keys: "ADD_BEFORE" and "ADD_AFTER".
142
+ """
143
+ before = kwargs.pop("ADD_BEFORE", "")
144
+ after = kwargs.pop("ADD_AFTER", "")
145
+
146
+ # Start with the base prompt. If empty, fallback to sys_prompt or join args.
147
+ prompt = self.prompt
148
+ if not prompt:
149
+ if self.sys_prompt: # In the case of sys_prompt provided, construct prompt from user input
150
+ prompt = "User instruction:"
151
+ if kwargs:
152
+ prompt += "\n" + "\n".join(f"{k}: {v}" for k, v in kwargs.items())
153
+ else:
154
+ raise ValueError("No prompt or system prompt provided.")
155
+
156
+ # Append any positional arguments
157
+ if args:
158
+ prompt = prompt + "\n" + "\n".join(args)
159
+
160
+ # Replace placeholders detected in the prompt (not system prompt).
161
+ for placeholder in self.placeholders:
162
+ if placeholder in kwargs:
163
+ prompt = prompt.replace(f"<<<{placeholder}>>>", str(kwargs.pop(placeholder)))
164
+
165
+ # Second pass: Replace any remaining kwargs.
166
+ for key, value in kwargs.items():
167
+ prompt = prompt.replace(f"<<<{key}>>>", str(value))
168
+
169
+ # Prepend or append additional text if provided.
170
+ if before:
171
+ prompt = before + "\n" + prompt
172
+ if after:
173
+ prompt = prompt + "\n" + after
174
+
175
+
176
+ return prompt
177
+
178
+ def _improve_prompt(self, replace: bool = False, use_prompt: str = "fabric", instruction: str = None, user_instruction: str = None) -> str:
179
+ """
180
+ Improves the prompt using a metaprompt.
181
+
182
+ Args:
183
+ replace (bool): Whether to replace the existing prompt.
184
+ use_prompt (str): Identifier for which metaprompt to use.
185
+ instruction (str): Custom instruction if provided.
186
+ user_instruction (str): Additional user instruction.
187
+
188
+ Returns:
189
+ str: The improved prompt.
190
+ """
191
+ if use_prompt == "fabric":
192
+ # Dynamically build an absolute path based on where POP.py is located
193
+ current_dir = path.dirname(path.abspath(__file__)) ## __file__ refers to the current file
194
+ file = path.join(current_dir, "prompts", "fabric-improve_prompt.md")
195
+
196
+ try:
197
+ with open(file, 'r', encoding='utf-8') as f:
198
+ instruction = f.read()
199
+ except FileNotFoundError:
200
+ raise FileNotFoundError(f"File not found: {file}")
201
+
202
+ meta_instruction = (
203
+ f"\nAdditional instruction:\n{user_instruction}\n"
204
+ "Ensure that original placeholders (<<<placeholder>>>) are preserved in the improved prompt and placed in a clear position."
205
+ "do not use any '<<<' or '>>>' in the improved prompt other than the original placeholder, and you have to show the placehold in the exact same order and amount of times as in the original prompt."
206
+ )
207
+
208
+ improved_prompt = self.execute(ADD_BEFORE=meta_instruction,
209
+ model="gpt-5",
210
+ sys=f"You are asked to improve the above 'Base system prompt' using the following instruction:\n{instruction}")
211
+
212
+ if use_prompt == "fabric":
213
+ improved_prompt = improved_prompt.split("# OUTPUT\n\n")[-1]
214
+
215
+ if replace:
216
+ self.sys_prompt = improved_prompt
217
+ return improved_prompt
218
+
219
+ def set_temperature(self, temperature: float) -> None:
220
+ """
221
+ Sets the temperature for the LLM.
222
+ """
223
+ self.temperature = temperature
224
+
225
+ def save(self, file_path: str) -> None:
226
+ """
227
+ Saves the prompt to a file.
228
+ """
229
+ with open(file_path, "w", encoding='utf-8') as file:
230
+ file.write(self.prompt)
231
+
232
+ def _get_place_holder(self) -> list:
233
+ """
234
+ Extracts placeholders (of the format <<<placeholder>>>) from the prompt or system prompt.
235
+ """
236
+ target_text = self.prompt if self.prompt else self.sys_prompt
237
+ if not target_text:
238
+ print("No prompt or system prompt provided.")
239
+ return []
240
+ placeholders = re.findall(r"<<<(.*?)>>>", target_text)
241
+ if placeholders:
242
+ print("Placeholders found:", placeholders)
243
+ else:
244
+ print("No placeholders found.")
245
+ return placeholders
246
+
247
+ def generate_schema(self,
248
+ description: str = None,
249
+ meta_prompt: str = None,
250
+ meta_schema: dict = None,
251
+ model: str = "gpt-5-mini",
252
+ save: bool = True
253
+ ) -> dict:
254
+ """
255
+ Instance method to generate a function schema from a natural language description
256
+ using the PromptFunction's prompt if no explicit description is given.
257
+ Also stores the generated schema to functions/ by default.
258
+
259
+ Args:
260
+ description (str): What the function should do.
261
+ meta_prompt (str): Optionally override the meta prompt, path to file.
262
+ meta_schema (dict): Optionally override the meta schema, path to file.
263
+ model (str): Which model to use.
264
+ save (bool): whether to store to functions/ directory
265
+
266
+ Returns:
267
+ dict: A valid OpenAI function tool schema.
268
+ """
269
+
270
+ # fallback to self.prompt if no description
271
+ if not description:
272
+ if self.prompt:
273
+ description = self.prompt
274
+ else:
275
+ raise ValueError(
276
+ "Description or instance prompt must be provided to generate a function schema."
277
+ )
278
+ print(description)
279
+ # fallback meta prompt from file
280
+ if meta_prompt is None:
281
+ try:
282
+ meta_prompt = PromptFunction.load_prompt(
283
+ "prompts/openai-json_schema_generator.md"
284
+ )
285
+ except FileNotFoundError:
286
+ raise FileNotFoundError(
287
+ "Meta prompt file 'prompts/openai-json_schema_generator.md' not found. "
288
+ "Either place it there or pass meta_prompt manually."
289
+ )
290
+ else:
291
+ meta_prompt = PromptFunction.load_prompt(meta_prompt)
292
+
293
+ # fallback meta schema from file
294
+ if meta_schema is None:
295
+ pass
296
+ else:
297
+ with open(meta_schema, "r", encoding="utf-8") as f:
298
+ meta_schema = json.load(f)
299
+
300
+ completion = self.client.chat_completion(
301
+ model=model,
302
+ response_format=meta_schema,
303
+ temperature=self.temperature,
304
+ messages=[
305
+ {
306
+ "role": "system",
307
+ "content": meta_prompt,
308
+ },
309
+ {
310
+ "role": "user",
311
+ "content": "Description:\n" + description,
312
+ },
313
+ ],
314
+ )
315
+ parsed_schema = json.loads(completion.choices[0].message.content)
316
+
317
+ # store to disk if requested
318
+ if save:
319
+ import os
320
+ os.makedirs("schemas", exist_ok=True)
321
+
322
+ prompts_name = parsed_schema["name"] if "name" in parsed_schema else "generated_schema"
323
+ safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", prompts_name)
324
+ file_path = os.path.join("schemas", f"{safe_name}.json")
325
+
326
+ with open(file_path, "w", encoding="utf-8") as f:
327
+ json.dump(parsed_schema, f, indent=2)
328
+ print(f"[generate_schema] Function schema saved to {file_path}")
329
+
330
+ return parsed_schema
331
+
332
+ @staticmethod
333
+ def load_prompt(file: str) -> str:
334
+ """
335
+ Loads a prompt from a file.
336
+ """
337
+ with open(file, 'r', encoding='utf-8') as f:
338
+ return f.read()
339
+
340
+ ##############################################
341
+ # Utility Function
342
+ ##############################################
343
+
344
+ def get_text_snapshot(web_url: str,
345
+ use_api_key: bool = True,
346
+ return_format: str = "default",
347
+ timeout: int = 0,
348
+ target_selector: list = None,
349
+ wait_for_selector: list = None,
350
+ exclude_selector: list = None,
351
+ remove_image: bool = False,
352
+ links_at_end: bool = False,
353
+ images_at_end: bool = False,
354
+ json_response: bool = False,
355
+ image_caption: bool = False,
356
+ cookie: str = None) -> str:
357
+ """
358
+ Fetch a text snapshot of the webpage using r.jina.ai.
359
+ """
360
+ target_selector = target_selector or []
361
+ wait_for_selector = wait_for_selector or []
362
+ exclude_selector = exclude_selector or []
363
+
364
+ headers = {}
365
+ api_key = 'Bearer ' + getenv("JINAAI_API_KEY") if use_api_key else None
366
+
367
+ header_values = {
368
+ "Authorization": api_key,
369
+ "X-Return-Format": None if return_format == "default" else return_format,
370
+ "X-Timeout": timeout if timeout > 0 else None,
371
+ "X-Target-Selector": ",".join(target_selector) if target_selector else None,
372
+ "X-Wait-For-Selector": ",".join(wait_for_selector) if wait_for_selector else None,
373
+ "X-Remove-Selector": ",".join(exclude_selector) if exclude_selector else None,
374
+ "X-Retain-Images": "none" if remove_image else None,
375
+ "X-With-Links-Summary": "true" if links_at_end else None,
376
+ "X-With-Images-Summary": "true" if images_at_end else None,
377
+ "Accept": "application/json" if json_response else None,
378
+ "X-With-Generated-Alt": "true" if image_caption else None,
379
+ "X-Set-Cookie": cookie if cookie else None
380
+ }
381
+
382
+ for key, value in header_values.items():
383
+ if value is not None:
384
+ headers[key] = value
385
+
386
+ try:
387
+ api_url = f"https://r.jina.ai/{web_url}"
388
+ response = requests.get(api_url, headers=headers)
389
+ response.raise_for_status()
390
+ return response.text
391
+ except requests.exceptions.RequestException as e:
392
+ return f"Error fetching text snapshot: {e}"
POP/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ from .POP import PromptFunction, get_text_snapshot
2
+ from .Embedder import Embedder
3
+ from .LLMClient import (
4
+ LLMClient,
5
+ OpenAIClient,
6
+ GeminiClient,
7
+ DeepseekClient,
8
+ LocalPyTorchClient,
9
+ DoubaoClient,
10
+ )
11
+
12
+ __all__ = [
13
+ "PromptFunction",
14
+ "get_text_snapshot",
15
+ "Embedder",
16
+ "LLMClient",
17
+ "OpenAIClient",
18
+ "GeminiClient",
19
+ "DeepseekClient",
20
+ "LocalPyTorchClient",
21
+ "DoubaoClient",
22
+ ]
@@ -0,0 +1,46 @@
1
+ ```markdown
2
+
3
+ Given a webpage URL, your task is to analyze its content and identify categories that are relevant for bedtime stories. Follow these instructions carefully:
4
+ 0. First focus on all the urls in the given snapshot. Choose those which corresponding to stories.
5
+
6
+ 1. **Extract Categories**: Look for content on the webpage that fits into common bedtime story themes.
7
+
8
+ 2. **Criteria for Selection**:
9
+ - The categories must be well-suited for bedtime reading to children.
10
+ - Each category should be broad enough to encompass multiple stories but specific enough to offer a clear theme or genre.
11
+ - The categories must be present in the website (i.e. need a url to access it)
12
+ - The url for each category should lead to a page of stories of that kind.
13
+ - It's usual for a website to have only 1 or 2 categories of story
14
+ - Some story website group stories by their audience's age, this can also work if no better choice (e.g. age 1-3, age 3-10, etc.)
15
+
16
+ 3. **Handling Unsuitable Content**:
17
+ - If the webpage content does not align with bedtime story themes or contains very few relevant stories, classify it as unsuitable.
18
+ - For webpages with no suitable content, use the response format: `{"error": "Refused: No suitable bedtime stories found."}`
19
+
20
+ 4. **Error Handling**:
21
+ - If the webpage URL is invalid or the content is inaccessible, provide an appropriate error message in the response.
22
+ - Make sure that you **do not** make up such urls, it must be on the websnap (very important).
23
+
24
+ 5. **Output Format**:
25
+ - Present your findings in a JSON object format.
26
+ - The key should be `categories`, followed by a list of identified categories relevant to bedtime stories.
27
+ - Ensure the output is clean, with categories listed clearly and concisely.
28
+
29
+ **Example Output for a Suitable Webpage**:
30
+
31
+ ```json
32
+ {
33
+ "categories": {"Fairy Tales": "url to page", "Fables": "url to page", "Mythology": "url to page"}
34
+ }
35
+ ```
36
+
37
+ **Example Output for an Unsuitable Webpage**:
38
+
39
+ ```json
40
+ {
41
+ "error": "Refused: No suitable bedtime stories found."
42
+ }
43
+ ```
44
+
45
+ Remember, your analysis should focus on identifying themes that will captivate and engage young listeners at bedtime, ensuring a delightful and imaginative end to their day.
46
+ ```
@@ -0,0 +1,71 @@
1
+ ### Enhanced AI Prompt for Story Extraction from Webpage Snapshots
2
+
3
+ **System Instructions:**
4
+ You are an advanced assistant tasked with extracting the core narrative content from webpage snapshots. Your input will be a textual snapshot of a webpage. Your goal is to meticulously identify and return the main story in a structured, clean format, while systematically ignoring extraneous sections such as advertisements, navigation links, or user comments. Specifically, you are to extract the title, author (when available), the main body of the story adhering to the structure provided below. Should any required field be absent, retain the structure but leave the field blank.
5
+
6
+ **Desired Output Structure:**
7
+ ```json
8
+ {
9
+ "title": "[Identify and insert the story's title here, if discernible.]",
10
+ "author": "[Insert the author's name here, if specified.]",
11
+ "content": "[Extract and format the main story text here, ensuring it is presented in clear paragraphs.]",
12
+ }
13
+ ```
14
+
15
+
16
+ ### Detailed Guidelines:
17
+ 1. **Title Extraction:**
18
+ Pinpoint and extract the most dominant heading (typically marked as `<h1>` in HTML) as the story's title.
19
+
20
+ 2. **Author Identification:**
21
+ Search for explicit mentions of the author's name, often indicated by "By [Author Name]" or contained within metadata elements.
22
+
23
+ 3. **Main Story Content:**
24
+ Concentrate on extracting the primary coherent text block, excluding standard sections like headers, footers, or "related articles" links.
25
+
26
+ 4. **Precision in Parsing:**
27
+ Employ robust parsing techniques to ensure the exclusion of irrelevant content (e.g., ads, comments) from the `story` field, maintaining focus on the narrative content.
28
+
29
+ ---
30
+
31
+ **Enhanced System Instructions for AI Processing:**
32
+ Upon receiving a webpage snapshot, your objectives are:
33
+ 1. Accurately identify and extract the title of the story, if it is explicitly mentioned.
34
+ 2. Locate and extract the author's name if it is clearly stated.
35
+ 3. Diligently extract the main story content, ensuring it is devoid of unrelated elements such as advertisements or user comments.
36
+ 4. If you decide this page doesn't actually contains stories, return empty str.
37
+
38
+ Format your findings into a JSON object as follows:
39
+ ```json
40
+ {
41
+ "title": "[Identify and insert the story's title here, if discernible.]",
42
+ "author": "[Insert the author's name here, if specified.]",
43
+ "content": "[Extract and format the main story text here, ensuring it is presented in clear paragraphs.]",
44
+ }
45
+
46
+ Ensure accuracy by omitting fields that lack clear data. Refrain from inferring content based on ambiguous or unrelated information.
47
+
48
+ **Example User Input:**
49
+ ```
50
+ Title: The Brave Little Tailor
51
+ By: Brothers Grimm
52
+
53
+ Once upon a time, a tailor was eating jam on toast by his window when a swarm of flies landed on it. He swatted them with his cloth, killing seven at once. Overjoyed, he made a belt reading "Seven at one blow."...
54
+
55
+ Related Stories:
56
+ - The Frog Prince
57
+ - Hansel and Gretel
58
+ ```
59
+
60
+ **Expected Refined Output:**
61
+ ```json
62
+ {
63
+ "title": "The Brave Little Tailor",
64
+ "author": "Brothers Grimm",
65
+ "content": "Once upon a time, a tailor was eating jam on toast by his window when a swarm of flies landed on it. He swatted them with his cloth, killing seven at once. Overjoyed, he made a belt reading 'Seven at one blow.'...",
66
+ }
67
+
68
+
69
+ ## fetch next page
70
+ Although **unlikely**, sometimes stories can be across multiple pages. If you checked the page and decide there's more of this story on next pages, store it in the next_page key. Other wise just leave it blank.
71
+ ```
@@ -0,0 +1,62 @@
1
+ ```markdown
2
+ # Extracting Bedtime Stories and URLs from Webpage Text
3
+
4
+ ## Goal
5
+ To accurately extract and organize bedtime story information from a given text version of a webpage, focusing on the story titles and their corresponding URLs, including the URL for the next page if available.
6
+
7
+ ## Input Details
8
+ - **Source Material**: A text representation of a webpage containing:
9
+ - Titles of bedtime stories.
10
+ - The original URLs for these stories.
11
+ - URLs to additional pages (e.g., "page 1", "page 2", or "next page"), if present.
12
+ - **Target Information**: The primary objective is to extract the titles of the stories and their direct URLs. Additionally, capture the URL for the next page when applicable.
13
+
14
+ ## Extraction Guidelines
15
+ 1. **Title and URL**:
16
+ - Isolate and retrieve only the titles of stories and their respective URLs, excluding any site navigation elements, advertisements, or non-relevant content.
17
+ - Trim any superfluous whitespace surrounding the titles or URLs.
18
+ - Make sure ALL of the URL and titles except those in ads or recommendation fields are obtained.
19
+
20
+ 2. **Guide line for locating next page**:
21
+ - Identify and include the URL for the next page of stories, if such exists. Usually those are corresponding to a number (2,3,4,etc) or some text such as "next page". **Do not** choose from other source instead of leaving it blank.
22
+ - **Don't return any urls that is not present in the text snapshot given you. **
23
+ - Look for hint for the last page, such as: no next page, and there's no url for any page number larger than the current page. if so return empty str
24
+
25
+ 3. **Handling Exceptions**:
26
+ - Should the text lack identifiable story titles, return a specific error message: `"Refused: No suitable bedtime stories found."`
27
+ - Bypass any story URLs that redirect incorrectly (e.g., back to the homepage) instead of to the actual story content.
28
+
29
+ 4. **Output Presentation**:
30
+ - Format the extracted data as a JSON object.
31
+ - Use `titles_and_urls` as a key for an array of dictionaries, each containing a story's `title` and `url`.
32
+ - Maintain clarity and conciseness in listing titles and URLs.
33
+ - Include a `next_page` key with the URL to the next page of stories if available; otherwise, leave this field empty.
34
+ - make sure the url to next page is actually links to next page.
35
+
36
+ **Example Output for Valid Webpage Content**:
37
+
38
+ ```json
39
+ {
40
+ "titles_and_urls": [
41
+ {
42
+ "title": "Cinderella (Classic)",
43
+ "url": "https://storiestogrowby.org/story/cinderella-fairy-tale-english-story-for-kids/"
44
+ },
45
+ {
46
+ "title": "The Contest of the Fairies",
47
+ "url": "https://storiestogrowby.org/story/rosanella-contest-fairies/"
48
+ }
49
+ // Additional stories here
50
+ ],
51
+ "next_page": ""
52
+ }
53
+ ```
54
+
55
+ **Example Output for Invalid Webpage Content**:
56
+
57
+ ```json
58
+ {
59
+ "error": "Refused: No suitable bedtime stories found."
60
+ }
61
+ ```
62
+ ```
@@ -0,0 +1,75 @@
1
+ # Guide to Building an Effective CLI AI Assistant
2
+
3
+ ## 1. Purpose Clarity
4
+ - **Define Capabilities**: Clearly outline the assistant's abilities and limitations.
5
+ - **Help Messages**: Offer concise, informative help messages via `--help` or `-h`.
6
+ - **Avoid Ambiguity**: Ensure outputs are clear and unambiguous.
7
+
8
+ ## 2. User-Friendly Interface
9
+ - **Simplicity**: Maintain a simple, intuitive interface.
10
+ - **Natural Commands**: Use natural language for commands and options.
11
+ - **Example**: `ask_GPT -f file.txt "Analyze this code"` instead of complex syntax.
12
+ - **Usage Examples**: Include examples in help messages for typical use cases.
13
+
14
+ ## 3. Error Handling
15
+ - **Input Validation**: Check inputs and provide clear error messages.
16
+ - **Example**: For invalid file paths, return: "Error: File not found. Please verify the path."
17
+ - **Stability**: Prevent crashes from invalid commands or inputs.
18
+
19
+ ## 4. Flexibility
20
+ - **Input Variability**: Accept text queries, file inputs, and stdin reading (e.g., `cat file.txt | ask_GPT`).
21
+ - **Configuration Options**: Support settings through environment variables or command-line arguments.
22
+
23
+ ## 5. Responsiveness
24
+ - **Quick Feedback**: Minimize response times.
25
+ - **Processing Indicators**: Inform users of longer processing times for extensive tasks.
26
+
27
+ ## 6. Transparency
28
+ - **Activity Insights**: Show backend processes when useful (e.g., "Querying GPT..." or "Analyzing 'input.txt'...").
29
+ - **Configuration Visibility**: Allow users to view (but not expose) sensitive settings like API keys.
30
+
31
+ ## 7. Customization
32
+ - **User Preferences**: Enable customization of output verbosity (`--verbose`), prompts, API parameters, and model settings.
33
+
34
+ ## 8. Security
35
+ - **Protect Sensitive Data**: Avoid revealing sensitive information in logs or error messages.
36
+ - **Input Sanitization**: Guard against exploits through proper input checks.
37
+ - **Data Handling Disclosure**: Clearly explain data processing practices to users.
38
+
39
+ ## 9. Output Quality
40
+ - **Readable Formatting**: Ensure terminal outputs are easy to read, with options for JSON or YAML formats for scripting.
41
+ - **Consistency and Actionability**: Provide uniform and useful responses.
42
+
43
+ ## 10. Integrability
44
+ - **CLI Ecosystem Compatibility**: Design for seamless integration with other CLI tools, supporting piping and redirection.
45
+ - **Script-Friendly Outputs**: Offer outputs that can be easily parsed by scripts.
46
+
47
+ ## 11. Accessibility
48
+ - **Inclusive Design**: Cater to both novices and experts with straightforward commands and advanced options.
49
+ - **Comprehensive Documentation**: Provide clear, accessible help documentation.
50
+
51
+ ## 12. Proactive Assistance
52
+ - **Error Correction Suggestions**: Offer fixes for common mistakes (e.g., "Did you mean '--file' instead of '--flie'?").
53
+ - **Insightful Recommendations**: Based on queries, suggest enhancements like code optimization tips.
54
+
55
+ ## 13. Extendability
56
+ - **Open for Extensions**: Facilitate the addition of new features or integrations by developers.
57
+ - **Well-Documented Codebase**: Ensure clear documentation for easy maintenance and extension.
58
+
59
+ ### Example Usage
60
+
61
+ ```bash
62
+ $ ask_GPT "What's the square root of 256?"
63
+ Response: 16
64
+
65
+ $ ask_GPT -f "example.py" "How can I improve this code?"
66
+ Processing file: example.py
67
+ Response:
68
+ - Add type hints for better readability.
69
+ - Avoid global variables to simplify debugging.
70
+ - Refactor `calculate_sum` for improved modularity.
71
+
72
+ $ cat large_text.txt | ask_GPT "Summarize this text"
73
+ Processing input from stdin...
74
+ Response: Highlights the role of CLI assistants in boosting developer efficiency.
75
+ ```