Files
wehub-resource-sync 35c9fb2445
CI Pipeline / code-quality (push) Waiting to run
CI Pipeline / test (macos-latest, 3.10) (push) Blocked by required conditions
CI Pipeline / test (macos-latest, 3.11) (push) Blocked by required conditions
CI Pipeline / test (macos-latest, 3.12) (push) Blocked by required conditions
CI Pipeline / test (macos-latest, 3.13) (push) Blocked by required conditions
CI Pipeline / test (ubuntu-latest, 3.10) (push) Blocked by required conditions
CI Pipeline / test (ubuntu-latest, 3.11) (push) Blocked by required conditions
CI Pipeline / test (ubuntu-latest, 3.12) (push) Blocked by required conditions
CI Pipeline / test (ubuntu-latest, 3.13) (push) Blocked by required conditions
CI Pipeline / test (windows-latest, 3.10) (push) Blocked by required conditions
CI Pipeline / test (windows-latest, 3.11) (push) Blocked by required conditions
CI Pipeline / test (windows-latest, 3.12) (push) Blocked by required conditions
CI Pipeline / test (windows-latest, 3.13) (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:32:40 +08:00

838 lines
37 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import ast
import csv
import json
import random
import pypdf
import markdown
import pandas as pd
from tqdm import tqdm
import openai
import tiktoken
import litellm
from groq import Groq
from litellm import completion
from .internal_api_completion import api_completion as internal_api_completion
from .proxy_call import api_completion as proxy_api_completion
from typing import Optional, List, Dict, Any
import logging
logger = logging.getLogger(__name__)
class SyntheticDataGeneration:
"""
A class for generating synthetic data using various AI models and processing different document types.
"""
def __init__(self):
"""
Initialize the SyntheticDataGeneration class with API clients for Groq, Gemini, and OpenAI.
"""
def generate_qna(self, text, question_type="simple", n=5, model_config=dict(), api_key=None, **kwargs):
"""
Generate questions based on the given text using the specified model and provider.
Uses batch processing for larger values of n to maintain response quality.
Args:
text (str): The input text to generate questions from.
question_type (str): The type of questions to generate ('simple', 'mcq', or 'complex').
n (int): The number of question/answer pairs to generate.
model_config (dict): Configuration for the model including provider and model name.
api_key (str, optional): The API key for the selected provider.
**kwargs: Additional keyword arguments.
Returns:
pandas.DataFrame: A DataFrame containing exactly n generated questions and answers.
Raises:
ValueError: If an invalid provider is specified or API key is missing.
"""
text_validity = self.validate_input(text)
if text_validity:
raise ValueError(text_validity)
BATCH_SIZE = 5 # Optimal batch size for maintaining response quality
provider = model_config.get("provider")
model = model_config.get("model")
api_base = model_config.get("api_base")
api_version = model_config.get("api_version")
# Initialize the appropriate client based on provider
self._initialize_client(provider, api_key, api_base, api_version, internal_llm_proxy=kwargs.get("internal_llm_proxy", None))
# Initialize progress bar
pbar = tqdm(total=n, desc="Generating QA pairs")
# Initial generation phase
num_batches = (n + BATCH_SIZE - 1) // BATCH_SIZE
all_responses = []
FAILURE_CASES = [
"Invalid API key provided",
"No connection adapters",
"Required API Keys are not set",
"litellm.BadRequestError",
"litellm.AuthenticationError",
"Max retries exceeded"
]
for _ in range(num_batches):
current_batch_size = min(BATCH_SIZE, n - len(all_responses))
if current_batch_size <= 0:
break
try:
system_message = self._get_system_message(question_type, current_batch_size)
if "internal_llm_proxy" in kwargs:
batch_df = self._generate_internal_response(text, system_message, model_config, kwargs)
else:
batch_df = self._generate_batch_response(text, system_message, provider, model_config, api_key, api_base)
if not batch_df.empty and len(batch_df) > 0:
all_responses.extend(batch_df.to_dict('records'))
pbar.update(len(batch_df))
except Exception as e:
print(f"Batch generation failed:{str(e)}")
if any(error in str(e) for error in FAILURE_CASES):
raise Exception(f"{e}")
else:
if "'utf-8' codec can't encode characters" in str(e):
print('Encountered non utf charactes, retrying with processed text')
text = str(text.encode('utf-8',errors='ignore'))
print(f"Retrying...")
continue
# Convert to DataFrame and remove duplicates
result_df = pd.DataFrame(all_responses)
result_df = result_df.drop_duplicates(subset=['Question'])
# Replenish phase - generate additional questions if needed due to duplicates
while (len(result_df) < n) and ((len(result_df) >= 1)):
questions_needed = n - len(result_df)
try:
system_message = self._get_system_message(question_type, questions_needed)
if "internal_llm_proxy" in kwargs:
additional_df = self._generate_internal_response(text, system_message, model_config, kwargs)
else:
additional_df = self._generate_batch_response(text, system_message, provider, model_config, api_key, api_base)
if not additional_df.empty and len(additional_df) > 0:
# Only add questions that aren't already in result_df
new_questions = additional_df[~additional_df['Question'].isin(result_df['Question'])]
if not new_questions.empty:
result_df = pd.concat([result_df, new_questions], ignore_index=True)
result_df = result_df.drop_duplicates(subset=['Question'])
pbar.update(len(new_questions))
except Exception as e:
print(f"Replenishment generation failed")
if any(error in str(e) for error in FAILURE_CASES):
raise Exception(f"{e}")
else:
print("An unexpected error occurred. Retrying...")
continue
pbar.close()
# Ensure exactly n rows and reset index starting from 1
final_df = result_df.head(n)
final_df.index = range(1, len(final_df) + 1)
return final_df
def _initialize_client(self, provider, api_key, api_base=None, api_version=None, internal_llm_proxy=None):
"""Initialize the appropriate client based on provider."""
if not provider:
raise ValueError("Model configuration must be provided with a valid provider and model.")
if provider == "groq":
if api_key is None and os.getenv("GROQ_API_KEY") is None:
raise ValueError("API key must be provided for Groq.")
self.groq_client = Groq(api_key=api_key or os.getenv("GROQ_API_KEY"))
elif provider == "gemini":
if api_key is None and os.getenv("GEMINI_API_KEY") is None and api_base is None and internal_llm_proxy is None:
raise ValueError("API key must be provided for Gemini.")
if api_key:
os.environ["GEMINI_API_KEY"] = api_key
# genai.configure(api_key=api_key or os.getenv("GEMINI_API_KEY"))
elif provider == "openai":
if api_key is None and os.getenv("OPENAI_API_KEY") is None and internal_llm_proxy is None:
raise ValueError("API key must be provided for OpenAI.")
openai.api_key = api_key or os.getenv("OPENAI_API_KEY")
elif provider == "azure":
if api_key is None and os.getenv("AZURE_API_KEY") is None and internal_llm_proxy is None:
raise ValueError("API key must be provided for Azure.")
litellm.api_key = api_key or os.getenv("AZURE_API_KEY")
if api_base is None and os.getenv("AZURE_API_BASE") is None and internal_llm_proxy is None:
raise ValueError("API Base must be provided for Azure.")
litellm.api_base = api_base or os.getenv("AZURE_API_BASE")
if api_version is None and os.getenv("AZURE_API_VERSION") is None and internal_llm_proxy is None:
raise ValueError("API version must be provided for Azure.")
litellm.api_version = api_version or os.getenv("AZURE_API_VERSION")
else:
raise ValueError(f"Provider is not recognized.")
def _generate_batch_response(self, text, system_message, provider, model_config, api_key, api_base):
"""Generate a batch of responses using the specified provider."""
MAX_RETRIES = 3
for attempt in range(MAX_RETRIES):
try:
if provider == "gemini" and api_base:
messages = [{'role': 'user', 'content': system_message + text}]
response = proxy_api_completion(messages=messages, model=model_config["model"], api_base=api_base)
# response = proxy_call.api_completion(messages=messages, model=model_config["model"], api_base=api_base)
return pd.DataFrame(ast.literal_eval(response[0]))
else:
return self._generate_llm_response(text, system_message, model_config, api_key)
except (json.JSONDecodeError, ValueError) as e:
if attempt == MAX_RETRIES - 1:
raise Exception(f"Failed to generate valid response after {MAX_RETRIES} attempts: {str(e)}")
continue
def _generate_internal_response(self, text, system_message, model_config, kwargs):
"""Generate response using internal API."""
messages = [{'role': 'user', 'content': system_message + text}]
return internal_api_completion(
messages=messages,
model_config=model_config,
kwargs=kwargs
)
def validate_input(self,text):
if not text.strip():
return 'Empty Text provided for qna generation. Please provide valid text'
encoding = tiktoken.encoding_for_model("gpt-4")
tokens = encoding.encode(text)
if len(tokens)<5:
return 'Very Small Text provided for qna generation. Please provide longer text'
return False
def _get_system_message(self, question_type, n):
"""
Get the appropriate system message for the specified question type.
Args:
question_type (str): The type of questions to generate ('simple', 'mcq', or 'complex').
n (int): The number of question/answer pairs to generate.
Returns:
str: The system message for the AI model.
Raises:
ValueError: If an invalid question type is specified.
"""
if question_type == 'simple':
return f'''Generate a set of {n} very simple questions answerable in a single phrase using the below text.
Only generate questions answerable from the text given, to cover all parts of the given document.
Also return the answers for the generated questions.
Return the response in a list of object format.
Each object in list should have Question and corresponding answer.
Do not return any extra strings. Return Generated text strictly in below format.
[{{"Question":"question,"Answer":"answer"}}]
'''
elif question_type == 'mcq':
return f'''Generate a set of {n} questions with 4 probable answers from the given text.
Only generate questions answerable from the text given, to cover all parts of the given document.
The options should not be longer than a phrase. There should be only 1 correct answer.
There should not be any ambiguity between correct and incorrect options.
Return the response in a list of object format.
Each object in list should have Question and a list of options.
Do not return any extra strings. Return Generated text strictly in below format.
[{{"Question":"question","Options":[option1,option2,option3,option4]}}]
'''
elif question_type == 'complex':
return f'''Can you generate a set of {n} complex questions answerable in long form from the below texts.
Only generate questions answerable from the text given, to cover all parts of the given document.
Make sure the questions are important and provide new information to the user.
Return the response in a list of object format. Enclose any quotes in single quote.
Do not use double quotes within questions or answers.
Each object in list should have Question and corresponding answer.
Do not return any extra strings. Return generated text strictly in below format.
[{{"Question":"question","Answer":"answers"}}]
'''
else:
raise ValueError("Invalid question type")
def _generate_llm_response(self, text, system_message, model_config, api_key=None):
"""
Generate questions using LiteLLM which supports multiple providers (OpenAI, Groq, Gemini, etc.).
Args:
text (str): The input text to generate questions from.
system_message (str): The system message for the AI model.
model_config (dict): Configuration dictionary containing model details.
Required keys:
- model: The model identifier (e.g., "gpt-4", "gemini-pro", "mixtral-8x7b-32768")
Optional keys:
- api_base: Custom API base URL if needed
- max_tokens: Maximum tokens in response
- temperature: Temperature for response generation
api_key (str, optional): The API key for the model provider.
Returns:
pandas.DataFrame: A DataFrame containing the generated questions and answers.
Raises:
Exception: If there's an error in generating the response.
"""
# Prepare the messages in the format expected by LiteLLM
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": text}
]
# Set up the completion parameters
completion_params = {
"model": model_config["model"],
"messages": messages,
"api_key": api_key
}
# Add optional parameters if they exist in model_config
if "api_base" in model_config:
completion_params["api_base"] = model_config["api_base"]
if "api_version" in model_config:
completion_params["api_version"] = model_config["api_version"]
if "max_tokens" in model_config:
completion_params["max_tokens"] = model_config["max_tokens"]
if "temperature" in model_config:
completion_params["temperature"] = model_config["temperature"]
if 'provider' in model_config:
completion_params['model'] = f'{model_config["provider"]}/{model_config["model"]}'
# Make the API call using LiteLLM
try:
response = completion(**completion_params)
except Exception as e:
if any(error in str(e).lower() for error in ["invalid api key", "incorrect api key", "unauthorized", "authentication"]):
raise ValueError(f"Invalid API key provided for {model_config.get('provider', 'the specified')} provider")
raise Exception(f"Error calling LLM API: {str(e)}")
# Extract the content from the response
content = response.choices[0].message.content
content = content.replace('\n', '').replace('```json','').replace('```', '').strip()
# Clean the response if needed (remove any prefix before the JSON list)
list_start_index = content.find('[')
if list_start_index != -1:
content = content[list_start_index:]
json_data = json.loads(content)
return pd.DataFrame(json_data)
def _generate_raw_llm_response(self, text, system_message: Optional[str] = None, model_config: Dict[str, Any] = dict(), api_key=None):
"""
Generate questions using LiteLLM which supports multiple providers (OpenAI, Groq, Gemini, etc.).
Args:
text (str): The input text to generate questions from.
system_message (str): The system message for the AI model.
model_config (dict): Configuration dictionary containing model details.
Required keys:
- model: The model identifier (e.g., "gpt-4", "gemini-pro", "mixtral-8x7b-32768")
Optional keys:
- api_base: Custom API base URL if needed
- max_tokens: Maximum tokens in response
- temperature: Temperature for response generation
api_key (str, optional): The API key for the model provider.
Returns:
pandas.DataFrame: A DataFrame containing the generated questions and answers.
Raises:
Exception: If there's an error in generating the response.
"""
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": text}
]
completion_params = {
"model": model_config.get("model", 'gpt-4o'),
"messages": messages,
"api_key": api_key
}
if "api_base" in model_config:
completion_params["api_base"] = model_config["api_base"]
if "api_version" in model_config:
completion_params["api_version"] = model_config["api_version"]
if "max_tokens" in model_config:
completion_params["max_tokens"] = model_config["max_tokens"]
if "temperature" in model_config:
completion_params["temperature"] = model_config["temperature"]
if 'provider' in model_config:
completion_params['model'] = f'{model_config["provider"]}/{model_config["model"]}'
try:
response = completion(**completion_params)
except Exception as e:
if any(error in str(e).lower() for error in ["invalid api key", "incorrect api key", "unauthorized", "authentication"]):
raise ValueError(f"Invalid API key provided for {model_config.get('provider', 'the specified')} provider")
raise Exception(f"Error calling LLM API: {str(e)}")
return response.choices[0].message.content
def _parse_response(self, response, provider):
"""
Parse the response from the AI model and return it as a DataFrame.
Args:
response (str): The response from the AI model.
provider (str): The AI provider used ('groq', 'gemini', or 'openai').
Returns:
pandas.DataFrame: The parsed response as a DataFrame.
"""
if provider == "openai":
data = response.choices[0].message.content
elif provider == "gemini":
data = response.candidates[0].content.parts[0].text
elif provider == "groq":
data = response.choices[0].message.content.replace('\n', '')
list_start_index = data.find('[') # Find the index of the first '['
substring_data = data[list_start_index:] if list_start_index != -1 else data # Slice from the list start
data = substring_data
elif provider == "azure":
data = response.choices[0].message.content.replace('\n', '')
list_start_index = data.find('[') # Find the index of the first '['
substring_data = data[list_start_index:] if list_start_index != -1 else data # Slice from the list start
data = substring_data
else:
raise ValueError("Invalid provider. Choose 'groq', 'gemini', 'azure' or 'openai'.")
try:
json_data = json.loads(data)
return pd.DataFrame(json_data)
except json.JSONDecodeError:
# If JSON parsing fails, return a DataFrame with a single column
return pd.DataFrame({'content': [data]})
def process_document(self, input_data):
"""
Process the input document and extract its content.
Args:
input_data (str): Either a file path or a string of text.
Returns:
str: The extracted text content from the document.
Raises:
ValueError: If the input is neither a valid file path nor a string of text.
"""
if isinstance(input_data, str):
if os.path.isfile(input_data):
# If input_data is a file path
_, file_extension = os.path.splitext(input_data)
try:
if file_extension.lower() == '.pdf':
return self._read_pdf(input_data)
elif file_extension.lower() == '.txt':
return self._read_text(input_data)
elif file_extension.lower() == '.md':
return self._read_markdown(input_data)
elif file_extension.lower() == '.csv':
return self._read_csv(input_data)
else:
raise ValueError(f"Unsupported file type: {file_extension}")
except Exception as e:
raise ValueError(f"Error reading the file. Upload a valid file. \n{e}")
else:
# If input_data is a string of text
return input_data
else:
raise ValueError("Input must be either a file path or a string of text")
def _read_pdf(self, file_path):
"""
Read and extract text from a PDF file.
Args:
file_path (str): The path to the PDF file.
Returns:
str: The extracted text content from the PDF.
"""
text = ""
with open(file_path, 'rb') as file:
pdf_reader = pypdf.PdfReader(file)
for page in pdf_reader.pages:
text += page.extract_text()
return text
def _read_text(self, file_path):
"""
Read the contents of a text file.
Args:
file_path (str): The path to the text file.
Returns:
str: The contents of the text file.
"""
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
def _read_markdown(self, file_path):
"""
Read and convert a Markdown file to HTML.
Args:
file_path (str): The path to the Markdown file.
Returns:
str: The HTML content converted from the Markdown file.
"""
with open(file_path, 'r', encoding='utf-8') as file:
md_content = file.read()
html_content = markdown.markdown(md_content)
return html_content
def _read_csv(self, file_path):
"""
Read and extract text from a CSV file.
Args:
file_path (str): The path to the CSV file.
Returns:
str: The extracted text content from the CSV, with each row joined and separated by newlines.
"""
text = ""
with open(file_path, 'r', encoding='utf-8') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
text += " ".join(row) + "\n"
return text
def get_supported_qna(self):
"""
Get a list of supported question types.
Returns:
list: A list of supported question types.
"""
return ['simple', 'mcq', 'complex']
def get_supported_providers(self):
"""
Get a list of supported AI providers.
Returns:
list: A list of supported AI providers.
"""
return ['gemini', 'openai','azure']
def _get_init_ex_gen_prompt(self):
prompt = '''
You are an expert example generator. Your task is to produce creative, relevant and varied examples according to the user instructions.
**Inputs**
User Instruction: The user will provide guidance on how to generate examples, possibly accompanied by their own examples.
User Examples[Optional]: The user may supply examples.
User Context[Optional]: The user may supply context to generate the examples from.
No of Examples: The total number of examples to produce.
**Steps to follow**
1. Carefully analyze the user's instruction
2. If user examples are provided, check whether the users instructions refer to them specifically.
3. If user context is provided, understand it thoroughly and identify relevant parts to generate examples.
4. Comply with the systems guidelines to generate examples, incorporating any user examples or user context as needed.
**Output Format**:
- Present examples in a multiline string with each line a separate example.
- Avoid markdown or special formatting.
- Omit any boilerplate texts.
**Instructions for Diversity**:
- Vary the examples by context, tone, and (if applicable) technical complexity.
- Include edge cases or unconventional scenarios.
- Ensure no two examples are conceptually identical.
**Final Notes**:
- Focus on both originality and practical relevance.
- Avoid repetitiveness in the examples.
'''
return prompt
def _get_iter_ex_gen_prompt(self):
prompt = '''
You are an expert example generator. Your task is to produce creative, relevant and varied examples according to the user instructions.
**Inputs**
User Instruction: The user will provide guidance on how to generate examples, possibly accompanied by their own examples.
User Examples[Optional]: The user may supply examples.
User Context[Optional]: The user may supply context to generate the examples from.
No of Examples: The total number of examples to produce.
Relevant Examples: Any examples that are relevant to the user's instruction.
Irrelevant Examples: Any examples that are not relevant to the user's instruction.
**Steps to follow**
1. Carefully analyze the user's instruction
2. If user examples are provided, check whether the users instructions refer to them specifically.
3. If user context is provided, understand it thoroughly and identify relevant parts to generate examples.
4. Review the relevant and irrelevant examples present, understanding the differences in them.
5. Comply with the user's instruction to generate examples, similar to relevant examples and dissimilar to irrelevant ones.
**Output Format**:
- Present examples in a multiline sting with each line a separate example.
- Avoid markdown or special formatting.
- Omit any boilerplate texts.
**Instructions for Diversity**:
- Vary the examples by context, tone, and (if applicable) technical complexity.
- Include edge cases or unconventional scenarios.
- Ensure no two examples are conceptually identical.
**Final Notes**:
- Focus on both originality and practical relevance.
- Avoid repetitiveness in the examples.
'''
return prompt
def _generate_examples_iter(
self,
user_instruction: str,
user_examples: Optional[List[str] | str] = None,
user_context: Optional[str] = None,
relevant_examples: List[str]=[],
irrelevant_examples: List[str]=[],
no_examples: Optional[int] = None,
model_config: Dict[str, Any] = dict(),
api_key: Optional[str] = None
):
if no_examples is None:
no_examples = 5
relevant_examples_str = '\n'.join(relevant_examples)
irrelevant_examples_str = '\n'.join(irrelevant_examples)
user_message = f'**User Instruction:** {user_instruction}'
user_message += f'\n\n**No of Examples:** {no_examples}'
if user_examples:
if isinstance(user_examples, str):
user_examples_str = user_examples
elif isinstance(user_examples, list):
user_examples_str = "\n".join(user_examples)
else:
raise ValueError(f'Expected string or list of strings as user_examples got {type(user_examples)}')
user_message += f"\n\n**User Examples:** \n{user_examples_str}"
if relevant_examples:
user_message += f'\n\n**Relevant Examples:** \n{relevant_examples_str}'
if irrelevant_examples:
user_message += f'\n\n**Irrelevant Examples:** \n{irrelevant_examples_str}'
if user_context:
user_message += f'\n\n**User Context:** \n{user_context}'
system_prompt = self._get_iter_ex_gen_prompt()
return self._generate_raw_llm_response(user_message, system_prompt, model_config=model_config, api_key=api_key)
def _generate_examples(
self,
user_instruction:str,
user_examples:Optional[List[str]|str]=None,
user_context: Optional[str] = None,
no_examples:Optional[int]=None,
model_config: Dict[str, Any] = dict(),
api_key: Optional[str] = None
):
if no_examples is None:
no_examples = 5
user_message = f"**User Instruction:** {user_instruction}"
if user_examples:
if isinstance(user_examples, str):
user_examples_str = user_examples
elif isinstance(user_examples, list):
user_examples_str = "\n".join(user_examples)
else:
raise ValueError(f'Expected string or list of strings as user_examples got {type(user_examples)}')
user_message += f"\n\n**User Examples:** \n{user_examples_str}"
if user_context:
user_message += f'\n\n**User Context:** \n{user_context}'
user_message += f'\n\n**No of Examples:** {no_examples}'
init_system_prompt = self._get_init_ex_gen_prompt()
return self._generate_raw_llm_response(user_message, init_system_prompt, model_config=model_config, api_key=api_key)
def _get_valid_examples(self, user_indices_str: str, examples: List[str]):
valid_examples = []
try:
user_indices = user_indices_str.strip().split(',')
for index_str in user_indices:
try:
index = int(index_str)
if index <= 0 or index > len(examples):
continue
except ValueError as e:
continue
valid_examples.append(examples[index-1])
except Exception as e:
print(f'Error: {e}')
return valid_examples
def generate_examples(
self,
user_instruction: str,
user_examples:Optional[List[str] | str] = None,
user_context: Optional[str] = None,
no_examples: Optional[int] = None,
model_config: Optional[Dict[str, Any]] = None,
api_key: Optional[str] = None,
max_iter: int = 0,
**kwargs
):
if not model_config:
model_config = {}
provider = model_config.get("provider")
api_base = model_config.get("api_base")
api_version = model_config.get("api_version")
self._initialize_client(provider, api_key, api_base, api_version, internal_llm_proxy=kwargs.get("internal_llm_proxy", None))
if no_examples is None:
no_examples = 5
assert no_examples >= 0, 'The number of examples cannot be less than 0'
relevant_examples = []
irrelevant_examples = []
max_relevant_examples = 5
max_irrelevant_examples = 10
while len(relevant_examples) <= max_relevant_examples or len(irrelevant_examples) <= max_irrelevant_examples:
if max_iter <= 0:
break
if len(relevant_examples) > max_relevant_examples:
relevant_examples = random.sample(relevant_examples, max_relevant_examples)
if len(irrelevant_examples) > max_irrelevant_examples:
irrelevant_examples = random.sample(irrelevant_examples, max_irrelevant_examples)
if relevant_examples or irrelevant_examples:
examples_str = self._generate_examples_iter(
user_instruction = user_instruction,
user_examples = user_examples,
relevant_examples = relevant_examples,
irrelevant_examples = irrelevant_examples,
model_config = model_config,
api_key = api_key
)
else:
examples_str = self._generate_examples(
user_instruction = user_instruction,
user_examples = user_examples,
user_context = user_context,
model_config = model_config,
api_key = api_key
)
examples = [example for example in examples_str.split('\n') if example.strip()]
print('Generated Examples:')
for i, example in enumerate(examples):
print(f'{i+1}. {example}')
relevant_indices = input('Enter the indices of relevant examples (comma-separated): ').strip()
if relevant_indices:
relevant_examples.extend(self._get_valid_examples(relevant_indices, examples))
irrelevant_indices = input('Enter the indices of irrelevant examples (comma-separated): ').strip()
if irrelevant_indices:
irrelevant_examples.extend(self._get_valid_examples(irrelevant_indices, examples))
max_iter -= 1
if len(relevant_examples) > max_relevant_examples:
fin_relevant_examples = random.sample(relevant_examples, max_relevant_examples)
else:
fin_relevant_examples = relevant_examples
if len(irrelevant_examples) > max_irrelevant_examples:
fin_irrelevant_examples = random.sample(irrelevant_examples, max_irrelevant_examples)
else:
fin_irrelevant_examples = irrelevant_examples
if relevant_examples or irrelevant_examples:
if len(relevant_examples) < no_examples:
more_no_examples = no_examples - len(relevant_examples)
final_examples_str = self._generate_examples_iter(
user_instruction = user_instruction,
user_examples = user_examples,
user_context = user_context,
relevant_examples = fin_relevant_examples,
irrelevant_examples = fin_irrelevant_examples,
no_examples = more_no_examples,
model_config = model_config,
api_key = api_key
)
final_examples = [example for example in final_examples_str.split('\n') if example.strip()]
final_examples.extend(relevant_examples)
else:
final_examples = random.sample(relevant_examples, no_examples)
else:
final_examples_str = self._generate_examples(
user_instruction = user_instruction,
user_examples = user_examples,
user_context = user_context,
no_examples = no_examples,
model_config = model_config,
api_key = api_key
)
final_examples = [example for example in final_examples_str.split('\n') if example.strip()]
return final_examples
def generate_examples_from_csv(
self,
csv_path: str,
dst_csv_path: Optional[str] = None,
no_examples: Optional[int] = None,
model_config: Optional[Dict[str, Any]] = None,
api_key: Optional[str] = None,
**kwargs
):
if no_examples is None:
no_examples = 5
assert no_examples >= 0, 'The number of examples cannot be less than 0'
df = pd.read_csv(csv_path)
assert 'user_instruction' in df.columns, 'The csv must have a column named user_instruction'
fin_df_list = []
for i, row in df.iterrows():
user_instruction = row['user_instruction']
user_examples = row.get('user_examples')
user_context = row.get('user_context')
row_dict = row.to_dict()
try:
examples = self.generate_examples(
user_instruction = user_instruction,
user_examples = user_examples,
user_context = user_context,
no_examples = no_examples,
model_config = model_config,
api_key = api_key
)
except Exception as e:
continue
for example in examples:
row_dict['generated_examples'] = example
fin_df_list.append(row_dict)
fin_df = pd.DataFrame(fin_df_list)
csv_file, csv_ext = os.path.splitext(csv_path)
if not dst_csv_path:
dst_csv_path = csv_file + '_with_examples' + csv_ext
dst_dir = os.path.dirname(dst_csv_path)
if dst_dir:
os.makedirs(dst_dir, exist_ok=True)
fin_df.to_csv(dst_csv_path)
logger.info(f'CSV with generated examples saved at {dst_csv_path}')
return dst_csv_path
# Usage:
# from synthetic_data_generation import SyntheticDataGeneration
# synthetic_data_generation = SyntheticDataGeneration()
# text = synthetic_data_generation.process_document(input_data=text_file)
# result = synthetic_data_generation.generate_question(text)
# supported_question_types = synthetic_data_generation.get_supported_question_types()
# supported_providers = synthetic_data_generation.get_supported_providers()