diff --git a/docs/examples/anonymization.mdx b/docs/examples/anonymization.mdx new file mode 100644 index 00000000..4b80d8f7 --- /dev/null +++ b/docs/examples/anonymization.mdx @@ -0,0 +1,75 @@ +--- +title: Data Anonymization +description: Use ControlFlow to anonymize sensitive information in text. +icon: user-secret +--- + +This example demonstrates how to use ControlFlow to create a task that anonymizes sensitive information in text. It showcases the use of custom types and context passing for data privacy tasks. + +## Code + +The following code creates a function that takes a text string containing sensitive information and returns an anonymized version along with the replacements made: + +```python +import controlflow as cf +from pydantic import BaseModel + +class AnonymizationResult(BaseModel): + original: str + anonymized: str + replacements: dict[str, str] + +def anonymize_text(text: str) -> AnonymizationResult: + return cf.run( + "Anonymize the given text by replacing personal information with generic placeholders", + result_type=AnonymizationResult, + context={"text": text} + ) +``` + +Now we can use this function to anonymize text containing sensitive information: + + +```python Example +original_text = "John Doe, born on 05/15/1980, lives at 123 Main St, New York. His email is john.doe@example.com." + +result = anonymize_text(original_text) +print(f"Original: {result.original}") +print(f"Anonymized: {result.anonymized}") +print("Replacements:") +for original, placeholder in result.replacements.items(): + print(f" {original} -> {placeholder}") +``` + +```text Output +Original: John Doe, born on 05/15/1980, lives at 123 Main St, New York. His email is john.doe@example.com. +Anonymized: [NAME], born on [DATE], lives at [ADDRESS], [CITY]. His email is [EMAIL]. +Replacements: + John Doe -> [NAME] + 05/15/1980 -> [DATE] + 123 Main St -> [ADDRESS] + New York -> [CITY] + john.doe@example.com -> [EMAIL] +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features: + +1. **[Pydantic models](/concepts/tasks/task-results#pydantic-models)**: We use a Pydantic model (`AnonymizationResult`) to define the structure of our anonymization result. This ensures that the task returns well-structured, consistent results including the original text, anonymized text, and replacements made. + + ```python + class AnonymizationResult(BaseModel): + original: str + anonymized: str + replacements: dict[str, str] + ``` + +2. **[Context passing](/concepts/tasks#context)**: We pass the original text as context to the task, providing all necessary information for the anonymization process. + + ```python + context={"text": text} + ``` + +By leveraging these ControlFlow features, we create an efficient and flexible data anonymization tool. This example demonstrates how ControlFlow can be used to build AI-powered privacy-enhancing workflows that can handle sensitive information with care. \ No newline at end of file diff --git a/docs/examples/code-explanation.mdx b/docs/examples/code-explanation.mdx new file mode 100644 index 00000000..f286a64f --- /dev/null +++ b/docs/examples/code-explanation.mdx @@ -0,0 +1,99 @@ +--- +title: Code Explanation +description: Use ControlFlow to generate natural language explanations of code snippets. +icon: code +--- + +This example demonstrates how to use ControlFlow to create a task that explains code snippets in natural language. It showcases the use of custom types and context passing for code documentation tasks. + +## Code + +The following code creates a function that takes a code snippet and its programming language, then returns an explanation of the code: + +```python +import controlflow as cf +from pydantic import BaseModel + +class CodeExplanation(BaseModel): + code: str + explanation: str + language: str + +def explain_code(code: str, language: str=None) -> CodeExplanation: + return cf.run( + f"Explain the following code snippet", + result_type=CodeExplanation, + context={"code": code, "language": language or 'auto-detect'} + ) +``` + +Now we can use this function to explain a code snippet: + + +```python Example +code_snippet = """ +def fibonacci(n): + if n <= 1: + return n + else: + return fibonacci(n-1) + fibonacci(n-2) +""" + +result = explain_code(code_snippet, "Python") +print(f"Code:\n{result.code}\n") +print(f"Explanation:\n{result.explanation}") +``` + +```text Output +Code: +def fibonacci(n): + if n <= 1: + return n + else: + return fibonacci(n-1) + fibonacci(n-2) + +Explanation: +This Python code defines a function called `fibonacci` that calculates +the nth number in the Fibonacci sequence using recursion. Here's a +breakdown of how it works: + +1. The function takes a single parameter `n`, which represents the +position in the Fibonacci sequence we want to calculate. + +2. There's a base case: if `n` is less than or equal to 1, the function +simply returns `n`. This handles the first two numbers in the Fibonacci +sequence (F(0) = 0 and F(1) = 1). + +3. For any other value of `n`, the function recursively calls itself twice: + - Once with `n-1` as the argument + - Once with `n-2` as the argument + +4. The results of these two recursive calls are added together and returned. + +This implementation follows the mathematical definition of the Fibonacci +sequence, where each number is the sum of the two preceding ones. However, +it's worth noting that this recursive approach can be inefficient for +large values of `n` due to repeated calculations. +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features: + +1. **[Pydantic models](/concepts/tasks/task-results#pydantic-models)**: We use a Pydantic model (`CodeExplanation`) to define the structure of our explanation result. This ensures that the task returns well-structured, consistent results including the original code, its explanation, and the programming language. + + ```python + class CodeExplanation(BaseModel): + code: str + explanation: str + language: str + ``` + +2. **[Context passing](/concepts/tasks#context)**: We pass both the code snippet and the programming language as context to the task, providing all necessary information for the explanation process. + + ```python + context={"code": code, "language": language} + ``` + +By leveraging these ControlFlow features, we create an efficient and flexible code explanation tool. This example demonstrates how ControlFlow can be used to build AI-powered documentation workflows that can help developers understand and explain code snippets in natural language. \ No newline at end of file diff --git a/docs/examples/examples-guide.md b/docs/examples/examples-guide.md new file mode 100644 index 00000000..9cc2db88 --- /dev/null +++ b/docs/examples/examples-guide.md @@ -0,0 +1,122 @@ +# Guide for Creating ControlFlow Examples + +This guide outlines the process for creating clear, informative, and consistent examples for ControlFlow documentation. Follow these steps to produce high-quality examples that showcase ControlFlow's features and capabilities. + +## 1. Example Structure + +Each example should follow this general structure: + +```markdown +--- +title: [Concise Title] +description: [Brief description of what the example demonstrates] +icon: [FontAwesome icon name] +--- + +[1-2 sentence introduction explaining the task and its relevance] + +## Code + +[Brief explanation of what the code does] + +```python +[Code block demonstrating the ControlFlow implementation] +``` + +[Usage examples in a CodeGroup, if applicable, as it creates a single tabbed view] + + +```python Code +[Full, copyable code for the example, including prints] +``` +```text Result +the result, including any intermiediate output that might be helpful +``` + + + +ALTERNATIVELY, use a code block for a single example including a commented result + +ALTERNATIVELY, use a CodeGroup for multiple examples with commented results + + + +## Key concepts + +[Explanation of key ControlFlow features demonstrated in the example] + +1. **[Feature Name](/link-to-docs)**: [Brief explanation of the feature] + + ```python + [Code snippet illustrating the feature] + ``` + +[2-3 sentences wrapping up the example and its significance] +``` + +## 2. Title and Description + +- Use a concise, descriptive title that clearly indicates the task or concept being demonstrated. +- Provide a brief (1-2 sentence) description that expands on the title and gives context. +- Choose an appropriate FontAwesome icon that represents the task or concept. + +## 3. Introduction + +- Begin with a 1-2 sentence introduction that explains the task or concept and its relevance in natural language processing or AI applications. +- If the example demonstrates multiple approaches or variations, briefly mention this. + +## 4. Code Section + +- Start with a brief explanation of what the code does and how it approaches the task. +- Present the main implementation code in a clear, well-commented Python code block. +- If there are multiple variations or approaches, present them sequentially, explaining the differences between each approach. +- Use type hints and follow PEP 8 style guidelines in the code. +- Import `controlflow as cf` at the beginning of each code block so it can be copied directly. +- Do not create agents unless you are demonstrating a specific feature (e.g. LLM selection, instructions, reusable tools, etc.) +- Try to write code that is as short as possible while still being clear and demonstrating the feature. +- Only use a flow if your tasks need to share history, otherwise just use a single task +- Do not import Dict or List from typing; use builtin dict or list instead + +## 5. Usage Examples + +- Provide 1-3 usage examples that demonstrate how to use the implemented function(s). +- Use the `` component to organize multiple examples. +- Include both the input and the expected output in the examples. +- Choose diverse and relevant examples that showcase different aspects of the implementation. + +## 6. Key Concepts + +- Identify 3-5 key ControlFlow features or concepts demonstrated in the example (if possible) +- For each concept: + 1. Provide a brief explanation of what the feature does and why it's important. + 2. Include a code snippet that illustrates the feature in use. + 3. Link to the relevant ControlFlow documentation for that feature. +- Arrange the concepts in order of importance or complexity. +- Do not consider controlflow basics like creating or running a task to be key concepts (or even "simple task creation") + +## 7. Conclusion + +- End with 2-3 sentences that wrap up the example, emphasizing its significance and how it showcases ControlFlow's capabilities. +- Mention any potential extensions or variations of the example that users might consider. + +## 8. Style and Tone + +- Use clear, concise language throughout the example. +- Maintain a professional and educational tone, avoiding overly casual language. +- Assume the reader has basic programming knowledge but may be new to ControlFlow. +- Use active voice and present tense where possible. + +## 9. Consistency + +- Ensure consistency in formatting, terminology, and style across all examples. +- Use the same naming conventions for variables and functions across related examples. +- Maintain a consistent level of detail and explanation across examples. + +## 10. Review and Refinement + +- After creating the example, review it for clarity, correctness, and completeness. +- Ensure all code is functional and produces the expected results. +- Check that all links to ControlFlow documentation are correct and up-to-date. +- Refine the language and explanations to be as clear and concise as possible. + +By following this guide, you'll create informative, consistent, and high-quality examples that effectively showcase ControlFlow's features and capabilities. These examples will help users understand how to implement various NLP and AI tasks using ControlFlow, encouraging adoption and proper usage of the framework. \ No newline at end of file diff --git a/docs/examples/generate-people.mdx b/docs/examples/generate-people.mdx new file mode 100644 index 00000000..7bac6625 --- /dev/null +++ b/docs/examples/generate-people.mdx @@ -0,0 +1,106 @@ +--- +title: Generate User Profiles +description: Use ControlFlow to generate test data based on a template. +icon: users +--- + +This example demonstrates how to use ControlFlow to create a task that generates test data based on a given template. It showcases the use of custom types and efficient batch processing. + +## Code + +The following code creates a function that takes a count then returns a list of generated user profiles that match a provide `result_type` template: + +```python +import controlflow as cf +from pydantic import BaseModel, Field + + +class UserProfile(BaseModel): + name: str = Field(description='The full name of the user') + age: int = Field(description='The age of the user, 20-60') + occupation: str = Field(description='The occupation of the user') + hobby: str + + +def generate_profiles(count: int) -> list[UserProfile]: + return cf.run( + f"Generate {count} user profiles", + result_type=list[UserProfile], + context={"count": count} + ) +``` + +Now we can generate some test data: + + +```python Example +test_data = generate_profiles(count=5) + +from rich import print +print(test_data) +``` + +```python Output +[ + UserProfile( + name='Emily Johnson', + age=27, + occupation='Software Engineer', + hobby='Painting' + ), + UserProfile( + name='Michael Smith', + age=34, + occupation='Marketing Manager', + hobby='Cycling' + ), + UserProfile( + name='Sarah Brown', + age=42, + occupation='Teacher', + hobby='Gardening' + ), + UserProfile( + name='David Wilson', + age=29, + occupation='Graphic Designer', + hobby='Photography' + ), + UserProfile( + name='Laura Davis', + age=50, + occupation='Chef', + hobby='Reading' + ) +] +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features: + +1. **[Pydantic models](/concepts/tasks/task-results#pydantic-models)**: We use a Pydantic model (`UserProfile`) to define the structure of our generated data. This ensures that the generation task returns well-structured, consistent results. + + ```python + class UserProfile(BaseModel): + name: str + age: int + occupation: str + hobby: str + ``` + +2. **[Batch processing](/concepts/tasks/task-results#collections)**: We generate multiple user profiles in a single task, which is more efficient than generating them individually. This is achieved by specifying `List[UserProfile]` as the `result_type`. + + ```python + result_type=List[UserProfile] + ``` + +3. **[Context passing](/concepts/tasks#context)**: We pass the desired count as context to the task, allowing the LLM to generate multiple data points based on the given parameters. + + ```python + context={"count": count} + ``` + + +By leveraging these ControlFlow features, we create an efficient and flexible test data generation tool. This example demonstrates how ControlFlow can be used to build AI-powered data generation workflows that can produce multiple data points in a single operation, based on customizable templates. This approach is particularly useful for creating diverse and realistic test datasets for various applications. \ No newline at end of file diff --git a/docs/examples/headline-categorization.mdx b/docs/examples/headline-categorization.mdx new file mode 100644 index 00000000..cb94b9ae --- /dev/null +++ b/docs/examples/headline-categorization.mdx @@ -0,0 +1,77 @@ +--- +title: Headline Categorization +description: Classify news headlines into predefined categories. +icon: list-check +--- + +Categorizing news headlines is a common task in content management and recommendation systems. This example demonstrates how to use ControlFlow to quickly build a headline classifier that categorizes news into predefined categories, showcasing the framework's ability to handle classification tasks with minimal code. + +## Code + +The following code creates a function that classifies a given news headline into one of five predefined categories. It uses ControlFlow's task running feature and leverages the power of language models to perform the classification. + +```python +import controlflow as cf +from langchain_openai import ChatOpenAI + +classifier = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini")) + +def classify_news(headline: str) -> str: + return cf.run( + "Classify the news headline into the most appropriate category", + agents=[classifier], + result_type=["Politics", "Technology", "Sports", "Entertainment", "Science"], + context={"headline": headline}, + ) +``` + +Now we can use this function to classify news headlines: + + +```python Example 1 +headline = "New AI Model Breaks Records in Language Understanding" +category = classify_news(headline) +print(f"Headline: {headline}") +print(f"Category: {category}") + +# Result: +# Headline: New AI Model Breaks Records in Language Understanding +# Category: Technology +``` +```python Example 2 +headline = "Scientists Discover Potentially Habitable Exoplanet" +category = classify_news(headline) +print(f"Headline: {headline}") +print(f"Category: {category}") + +# Result: +# Headline: Scientists Discover Potentially Habitable Exoplanet +# Category: Science +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features that enable quick development of classification tools: + +1. **[Agents](/concepts/agents)**: We create an agent with a specific LLM model (GPT-4o mini) to perform the headline classification. + + ```python + classifier = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini")) + ``` + +2. **[Result types](/concepts/tasks/task-results)**: We use a list of strings as the `result_type` to constrain the output to one of the predefined categories. This ensures that the classification result is always one of the specified options. + + ```python + result_type=["Politics", "Technology", "Sports", "Entertainment", "Science"] + ``` + +3. **[Context passing](/concepts/tasks#context)**: The `context` parameter is used to pass the input headline to the task. + + ```python + context={"headline": headline} + ``` + +By leveraging these ControlFlow features, we can create a powerful headline classifier with just a few lines of code. This example demonstrates how ControlFlow simplifies the process of building and deploying classification tools, making it easier for developers to incorporate advanced language processing capabilities into their applications. + +The use of predefined categories in the `result_type` is particularly noteworthy, as it allows us to constrain the model's output to a specific set of options. This is useful in many real-world scenarios where we need to map inputs to a fixed set of categories. \ No newline at end of file diff --git a/docs/examples/named-entity-recognition.mdx b/docs/examples/named-entity-recognition.mdx new file mode 100644 index 00000000..47a845fe --- /dev/null +++ b/docs/examples/named-entity-recognition.mdx @@ -0,0 +1,120 @@ +--- +title: Named Entity Recognition +description: Extract named entities from text using ControlFlow. +icon: landmark-dome +--- + +Named Entity Recognition (NER) is a crucial task in natural language processing, used to identify named entities (such as persons, organizations, locations) in text. This example demonstrates how to implement a simple NER system using ControlFlow and a GPT-4o mini model, showcasing two different approaches: extracting a simple list of entities and categorizing entities by type. + +## Code + +First, let's implement a function that extracts a simple list of entities: + +```python +import controlflow as cf +from langchain_openai import ChatOpenAI +from typing import List + +extractor = cf.Agent( + name="Named Entity Recognizer", + model=ChatOpenAI(model="gpt-4o-mini"), +) + +def extract_entities(text: str) -> List[str]: + return cf.run( + "Extract all named entities from the text", + agents=[extractor], + result_type=List[str], + context={"text": text}, + ) +``` + +We can call this function on any text to extract all named entities: + +```python Simple extraction +text = "Apple Inc. is planning to open a new store in New York City next month." +entities = extract_entities(text) + +print(entities) +# Result: +# ['Apple Inc.', 'New York City'] +``` + +Now, let's modify our function to categorize the entities it extracts. We do this by changing the result type to a dictionary and providing detailed instructions about the types of entities we want to extract: + +```python +def extract_categorized_entities(text: str) -> Dict[str, List[str]]: + return cf.run( + "Extract named entities from the text and categorize them", + instructions=""" + Return a dictionary with the following keys: + - 'persons': List of person names + - 'organizations': List of organization names + - 'locations': List of location names + - 'dates': List of date references + - 'events': List of event names + Only include keys if entities of that type are found in the text. + """, + agents=[extractor], + result_type=Dict[str, List[str]], + context={"text": text}, + ) +``` + +Here's how we can use this function to perform NER on some example texts: + +```python Categorized extraction +text = "In 1969, Neil Armstrong became the first person to walk on the Moon during the Apollo 11 mission." +entities = extract_categorized_entities(text) + +print(entities) +# Result: +# { +# 'persons': ['Neil Armstrong'], +# 'locations': ['Moon'], +# 'dates': ['1969'], +# 'events': ['Apollo 11 mission'] +# } +``` + +## Key concepts + +This implementation showcases several important ControlFlow features that enable quick development of NLP tools: + +1. **[Agents](/concepts/agents)**: We create an agent with a specific LLM model (GPT-4o mini) to perform the named entity recognition. + + ```python + extractor = cf.Agent( + name="Named Entity Recognizer", + model=ChatOpenAI(model="gpt-4o-mini"), + ) + ``` + +2. **[Flexible result types](/concepts/tasks/task-results)**: We demonstrate two different result types: a simple list of strings and a dictionary of categorized entities. This flexibility allows us to adapt the output structure to our specific needs. + + ```python + result_type=List[str] + # or + result_type=Dict[str, List[str]] + ``` + +3. **[Detailed instructions](/concepts/tasks#instructions)**: In the categorized version, we provide detailed instructions to guide the model in structuring its output. This allows us to define a specific schema for the results without changing the underlying model. + + ```python + instructions=""" + Return a dictionary with the following keys: + - 'persons': List of person names + - 'organizations': List of organization names + ... + """ + ``` + +4. **[Context passing](/concepts/tasks#context)**: The `context` parameter is used to pass the input text to the task. + + ```python + context={"text": text} + ``` + +By leveraging these ControlFlow features, we can create powerful NER tools with minimal code. This example demonstrates how ControlFlow simplifies the process of building and deploying NLP tools, making it easier for developers to incorporate advanced language processing capabilities into their applications. + +The ability to easily switch between different output structures (list vs. categorized dictionary) showcases the flexibility of ControlFlow in adapting to various NLP task requirements. \ No newline at end of file diff --git a/docs/examples/pineapple-pizza.mdx b/docs/examples/pineapple-pizza.mdx new file mode 100644 index 00000000..231d91ee --- /dev/null +++ b/docs/examples/pineapple-pizza.mdx @@ -0,0 +1,41 @@ +--- +title: Pineapple-on-Pizza Debate +--- + +This example demonstrates a debate between two agents. One agent plays the role of an eternal optimist, while the other plays the role of an eternal pessimist. The debate is moderated by a third agent who decides whose argument is more compelling. + +```python Code +import controlflow as cf + +optimist = cf.Agent( + name="Half-full", + instructions="You are an eternal optimist.", +) +pessimist = cf.Agent( + name="Half-empty", + instructions="You are an eternal pessimist.", +) +# create an agent that will decide who wins the debate +moderator = cf.Agent(name="Moderator") + + +@cf.flow +def demo(topic: str): + cf.run( + "Have a debate about the topic.", + instructions="Each agent should take at least two turns.", + agents=[optimist, pessimist], + context={"topic": topic}, + ) + + winner: cf.Agent = cf.run( + "Whose argument do you find more compelling?", + agents=[moderator], + result_type=[optimist, pessimist], + ) + + print(f"{winner.name} wins the debate!") + + +demo("pineapple on pizza") +``` \ No newline at end of file diff --git a/docs/examples/rock-paper-scissors.mdx b/docs/examples/rock-paper-scissors.mdx index 7cd9f6d4..824bb4fb 100644 --- a/docs/examples/rock-paper-scissors.mdx +++ b/docs/examples/rock-paper-scissors.mdx @@ -1,61 +1,79 @@ --- title: Rock, Paper, Scissors +description: Play rock, paper, scissors against an AI... without letting it cheat. +icon: hand-scissors +# mode: wide --- -How do you play rock, paper, scissors against an AI without letting it cheat? +Creating a fair game of rock, paper, scissors against an AI opponent presents an interesting challenge: how do we prevent the AI from "cheating" by reading the player's choice before making its own? This example demonstrates how ControlFlow's features can be used to create a fair and engaging game while showcasing several key concepts of the framework. -This example uses a private conversation with a dedicated "Helper" agent to collect the user's move in secret. Only after both the user's and AI's choices are made do we reveal the user's choice to the AI to determine the winner. +## Code +The following code creates a function that plays rock, paper, scissors in a loop. Each round, it collects the user's move in a private context, then the AI's move in another private context, and finally reports the result and asks if the user wants to continue. This structure ensures that neither player can access the other's choice prematurely. ```python import controlflow as cf -# Create an agent to privately collect the user's score +@cf.flow +def rock_paper_scissors(): + """Play rock, paper, scissors against an AI.""" + play_again = True -user_helper = cf.Agent( - "RPS Helper", - instructions=""" - Get the user's choice of rock, paper, or scissors. - You can assure them that you won't share their answer - with the AI opponent. - """, -) + while play_again: + # Get the user's choice on a private thread + with cf.Flow(): + user_choice = cf.run( + "Get the user's choice", + result_type=["rock", "paper", "scissors"], + interactive=True, + ) + + # Get the AI's choice on a private thread + with cf.Flow(): + ai_choice = cf.run( + "Choose rock, paper, or scissors", + result_type=["rock", "paper", "scissors"], + ) -# Create tasks for getting the user's choice, -# the AI's choice, and reporting the score + # Report the score and ask if the user wants to play again + play_again = cf.run( + "Report the score to the user and see if they want to play again.", + interactive=True, + context={"user_choice": user_choice, "ai_choice": ai_choice}, + result_type=bool + ) -@cf.task(interactive=True, agents=[user_helper]) -def get_user_choice() -> ["rock", "paper", "scissors"]: - """Ask the user to choose rock, paper, or scissors.""" +rock_paper_scissors() +``` -@cf.task -def get_ai_choice() -> ["rock", "paper", "scissors"]: - """Choose rock paper or scissors""" +Try running this example to see how ControlFlow manages the game flow and maintains fairness in this AI vs. human contest! -@cf.task(interactive=True) -def report_score(user_choice, ai_choice) -> bool: - """ - Tell the user if they won, the overall score, - then find out if they want to play again. - """ +## Key concepts -@cf.flow -def rock_paper_scissors(): - keep_playing = True +This implementation showcases how ControlFlow can be used to create interactive, multi-step processes with controlled information flow. By using separate Flows for the player and AI choices, we ensure that the AI can't "cheat" by accessing the player's choice prematurely. The use of structured tasks, result types, and context passing allows for a clean and intuitive game logic, while the interactive features enable seamless player involvement. - # keep playing as long as the user wants - while keep_playing: - - # use a nested flow to keep the user's choice private - with cf.Flow(): - user_choice = get_user_choice() +1. **[Flows](/concepts/flows)**: We use separate Flows to create isolated contexts for the player's and AI's choices. This ensures that neither can access the other's decision until both have been made. + + ```python + with cf.Flow(): + user_choice = cf.run(...) + ``` +2. **[Interactivity](/patterns/interactivity)**: The `interactive=True` parameter allows tasks to interact with the user, essential for getting the player's input. + + ```python + user_choice = cf.run(..., interactive=True, ...) + ``` + +3. **[Result types](/concepts/tasks/task-results)**: We use `result_type` to ensure that choices are valid and properly structured. This helps maintain the integrity of the game. + + ```python + result_type=["rock", "paper", "scissors"] + ``` - # get the ai's choice - ai_choice = get_ai_choice() +4. **[Context passing](/concepts/tasks#context)**: The `context` parameter allows us to share information between tasks, crucial for determining the winner based on both players' choices. - # report the score and ask if the user wants to play again - keep_playing = report_score(user_choice, ai_choice) + ```python + context={"user_choice": user_choice, "ai_choice": ai_choice} + ``` -if __name__ == "__main__": - rock_paper_scissors() -```` \ No newline at end of file +By leveraging these ControlFlow features, we can create a multi-step process that maintains fairness while allowing for engaging interaction between the AI and the player. \ No newline at end of file diff --git a/docs/examples/sentiment-classifier.mdx b/docs/examples/sentiment-classifier.mdx new file mode 100644 index 00000000..4a520066 --- /dev/null +++ b/docs/examples/sentiment-classifier.mdx @@ -0,0 +1,78 @@ +--- +title: Sentiment Classifier +description: Use GPT-4o mini to quickly build a sentiment classifier. +icon: face-laugh-beam +--- + +Sentiment analysis is a common natural language processing task that involves determining the emotional tone of a piece of text. This example demonstrates how to use ControlFlow to quickly build a sentiment classifier using GPT-4o mini, showcasing the framework's ability to create powerful NLP tools with minimal code. + +## Code + +The following code creates a function that classifies the sentiment of a given text on a scale from 0 (very negative) to 1 (very positive). It uses a GPT-4o mini model for classification and leverages ControlFlow's task running and result validation features. + +```python +import controlflow as cf +from controlflow.tasks.validators import between +from langchain_openai import ChatOpenAI + +optimist = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini")) + +def sentiment(text: str) -> float: + return cf.run( + "Classify the sentiment of the text as a value between 0 and 1", + agents=[optimist], + result_type=float, + result_validator=between(0, 1), + context={"text": text}, + ) +``` + +Now we can run this function on any text: + + +```python Example 1 +sentiment("I love ControlFlow!") + +# Result: 1.0 +``` +```python Example 2 +sentiment( + """ + Far out in the uncharted backwaters of the unfashionable end of + the western spiral arm of the Galaxy lies a small unregarded yellow sun. + Orbiting this at a distance of roughly ninety-two million miles is an utterly + insignificant little blue-green planet whose ape-descended life forms are so + amazingly primitive that they still think digital watches are a pretty neat + idea. This planet has – or rather had – a problem, which was this: most of + the people living on it were unhappy for pretty much of the time. + """ +) +# Result: 0.2 +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features that enable quick development of NLP tools: + +1. **[Agents](/concepts/agents)**: We create an agent with a specific LLM model (GPT-4o mini) to perform the sentiment analysis. + + ```python + optimist = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini")) + ``` + +3. **[Result types](/concepts/tasks/task-results)**: We specify `result_type=float` to ensure the sentiment score is returned as a float value. + +4. **[Result validation](/concepts/tasks/task-results#result-validators)**: The `result_validator` parameter is used with the `between()` function to ensure the result falls within the expected range. + + ```python + result_validator=between(0, 1) + ``` + +5. **[Context passing](/concepts/tasks#context)**: The `context` parameter is used to pass the input text to the task. + + ```python + context={"text": text} + ``` + +By leveraging these ControlFlow features, we can create a powerful sentiment classifier with just a few lines of code. This example demonstrates how ControlFlow can simplify the process of building and deploying NLP tools, making it easier for developers to incorporate advanced language processing capabilities into their applications. \ No newline at end of file diff --git a/docs/examples/standardize-addresses.mdx b/docs/examples/standardize-addresses.mdx new file mode 100644 index 00000000..06b770a5 --- /dev/null +++ b/docs/examples/standardize-addresses.mdx @@ -0,0 +1,108 @@ +--- +title: Standardize Place Names +description: Use ControlFlow to efficiently standardize multiple place names into consistent postal addresses. +icon: map-pin +--- + +This example demonstrates how to use ControlFlow to create a task that standardizes multiple place names into consistent postal addresses in a single operation. It showcases the use of custom types and efficient batch processing. + +## Code + +The following code creates a function that takes a list of place names and returns a list of standardized addresses: + +```python +import controlflow as cf +from pydantic import BaseModel +from typing import List + +class StandardAddress(BaseModel): + city: str + state: str + country: str = "USA" + +def standardize_addresses(place_names: List[str]) -> List[StandardAddress]: + return cf.run( + "Standardize the given place names into consistent postal addresses", + result_type=List[StandardAddress], + context={"place_names": place_names} + ) +``` + +You can use this function to standardize a list of place names: + + +```python Example +place_names = [ + "NYC", "New York, NY", "Big Apple", + "Los Angeles, California", "LA", + "San Fran", "The Windy City" +] + +standardized_addresses = standardize_addresses(place_names) + +for original, standard in zip(place_names, standardized_addresses): + print(f"Original: {original}") + print(f"Standardized: {standard}") + print() +``` + +```text Output +Original: NYC +Standardized: StandardAddress(city='New York City', state='NY', country='USA') + +Original: New York, NY +Standardized: StandardAddress(city='New York City', state='NY', country='USA') + +Original: Big Apple +Standardized: StandardAddress(city='New York City', state='NY', country='USA') + +Original: Los Angeles, California +Standardized: StandardAddress(city='Los Angeles', state='CA', country='USA') + +Original: LA +Standardized: StandardAddress(city='Los Angeles', state='CA', country='USA') + +Original: San Fran +Standardized: StandardAddress(city='San Francisco', state='CA', country='USA') + +Original: The Windy City +Standardized: StandardAddress(city='Chicago', state='IL', country='USA') +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features: + +1. **[Pydantic models](/concepts/tasks/task-results#pydantic-models)**: We use a Pydantic model (`StandardAddress`) to define the structure of our standardized addresses. This ensures that the standardization task returns well-structured, consistent results. + + ```python + class StandardAddress(BaseModel): + city: str + state: str + country: str = "USA" + ``` + +2. **[Batch processing](/concepts/tasks/task-results#collections)**: We process a list of place names in a single task, which is more efficient than processing them individually. This is achieved by specifying `List[StandardAddress]` as the `result_type`. + + ```python + result_type=List[StandardAddress] + ``` + +3. **[Context passing](/concepts/tasks#context)**: We pass the entire list of place names as context to the task, allowing the LLM to process all inputs at once. + + ```python + context={"place_names": place_names} + ``` + +4. **[Simple task creation](/concepts/tasks/creating-tasks)**: We use `cf.run()` to create and execute a task in a single step, simplifying our code. + + ```python + return cf.run( + "Standardize the given place names into consistent postal addresses", + result_type=List[StandardAddress], + context={"place_names": place_names} + ) + ``` + +By leveraging these ControlFlow features, we create an efficient and straightforward address standardization tool. This example demonstrates how ControlFlow can be used to build AI-powered data processing workflows that handle multiple inputs in a single operation, improving performance and reducing costs. \ No newline at end of file diff --git a/docs/examples/summarization.mdx b/docs/examples/summarization.mdx new file mode 100644 index 00000000..d96e9152 --- /dev/null +++ b/docs/examples/summarization.mdx @@ -0,0 +1,100 @@ +--- +title: Text Summarization +description: Generate concise summaries of longer texts. +icon: file-lines +--- + +Text summarization is a valuable tool for quickly extracting key information from longer documents. This example demonstrates how to use ControlFlow to create a text summarization function that not only produces a concise summary but also extracts key points, all in a single pass. + +## Code + +The following code creates a function that summarizes a given text and extracts key points. It uses ControlFlow's task running feature and leverages Pydantic for structured output. + +```python +import controlflow as cf +from pydantic import BaseModel + +class Summary(BaseModel): + summary: str + key_points: list[str] + +def summarize_text(text: str, max_words: int = 100) -> Summary: + return cf.run( + f"Summarize the given text in no more than {max_words} words and list key points", + result_type=Summary, + context={"text": text}, + ) +``` + +Let's use this function to summarize a longer text: + + +```python Example +long_text = """ + The Internet of Things (IoT) is transforming the way we interact with our + environment. It refers to the vast network of connected devices that collect + and share data in real-time. These devices range from simple sensors to + sophisticated wearables and smart home systems. The IoT has applications in + various fields, including healthcare, agriculture, and urban planning. In + healthcare, IoT devices can monitor patients remotely, improving care and + reducing hospital visits. In agriculture, sensors can track soil moisture and + crop health, enabling more efficient farming practices. Smart cities use IoT to + manage traffic, reduce energy consumption, and enhance public safety. However, + the IoT also raises concerns about data privacy and security, as these + interconnected devices can be vulnerable to cyber attacks. As the technology + continues to evolve, addressing these challenges will be crucial for the + widespread adoption and success of IoT. + """ + +result = summarize_text(long_text) +print(result.summary) +print("\nKey Points:") +for point in result.key_points: + print(f"- {point}") +``` + +```text Result +The Internet of Things (IoT) is a network of connected devices that collect and +share data in real-time, transforming various fields such as healthcare, +agriculture, and urban planning. While IoT offers numerous benefits, including +remote patient monitoring, efficient farming, and smart city management, it +also raises concerns about data privacy and security. + +Key Points: +- IoT is a network of connected devices sharing real-time data +- Applications include healthcare, agriculture, and urban planning +- Benefits include remote patient monitoring and efficient resource management +- Raises concerns about data privacy and security +- Addressing challenges is crucial for widespread adoption +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features that enable quick development of advanced text processing tools: + +1. **[Structured outputs](/concepts/tasks/task-results)**: We use a Pydantic model (`Summary`) as the `result_type` to define the structure of our output. This ensures that the summarization task returns both a summary and a list of key points in a well-defined format. + + ```python + class Summary(BaseModel): + summary: str + key_points: list[str] + + result_type=Summary + ``` + +2. **[Context passing](/concepts/tasks#context)**: The `context` parameter is used to pass the input text and maximum word count to the task. + + ```python + context={"text": text} + ``` + +3. **[Dynamic instructions](/concepts/tasks#instructions)**: We include the `max_words` parameter in the task instruction, allowing for flexible control over the summary length. + + ```python + f"Summarize the given text in no more than {max_words} words and list key points" + ``` + +By leveraging these ControlFlow features, we can create a powerful text summarization tool with just a few lines of code. This example demonstrates how ControlFlow simplifies the process of building and deploying advanced NLP tools, making it easier for developers to incorporate complex language processing capabilities into their applications. + +The use of a Pydantic model for the output is particularly noteworthy, as it allows us to define a clear structure for our summarization results. This structured output makes it easy to work with the summary and key points separately in downstream tasks or when presenting the information to users. \ No newline at end of file diff --git a/docs/examples/translation.mdx b/docs/examples/translation.mdx new file mode 100644 index 00000000..5242a123 --- /dev/null +++ b/docs/examples/translation.mdx @@ -0,0 +1,66 @@ +--- +title: Text Translation +description: Use ControlFlow to translate text from one language to another. +icon: language +--- + +This example demonstrates how to use ControlFlow to create a task that translates text from one language to another. It showcases the use of custom types and context passing for language translation tasks. + +## Code + +The following code creates a function that takes a text string and a target language, then returns a translation result: + +```python +import controlflow as cf +from pydantic import BaseModel + +class TranslationResult(BaseModel): + translated: str + target_language: str + +def translate_text(text: str, target_language: str) -> TranslationResult: + return cf.run( + f"Translate the given text to {target_language}", + result_type=TranslationResult, + context={"text": text, "target_language": target_language} + ) +``` + +Now we can use this function to translate text: + + +```python Example +original_text = "Hello, how are you?" +target_language = "French" + +result = translate_text(original_text, target_language) +print(f"Original: {original_text}") +print(f"Translated ({result.target_language}): {result.translated}") +``` + +```text Output +Original: Hello, how are you? +Translated (French): Bonjour, comment allez-vous ? +``` + + +## Key concepts + +This implementation showcases several important ControlFlow features: + +1. **[Pydantic models](/concepts/tasks/task-results#pydantic-models)**: We use a Pydantic model (`TranslationResult`) to define the structure of our translation result. This ensures that the translation task returns well-structured, consistent results. + + ```python + class TranslationResult(BaseModel): + original: str + translated: str + target_language: str + ``` + +2. **[Context passing](/concepts/tasks#context)**: We pass both the original text and the target language as context to the task, providing all necessary information for the translation. + + ```python + context={"text": text, "target_language": target_language} + ``` + +By leveraging these ControlFlow features, we create an efficient and flexible text translation tool. This example demonstrates how ControlFlow can be used to build AI-powered language processing workflows that can handle translation tasks with ease. \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 6c5ed467..7ccfbd48 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -70,13 +70,21 @@ ] }, { - "group": "Library", + "group": "Core LLM Operations", "pages": [ - "examples/library" + "examples/sentiment-classifier", + "examples/headline-categorization", + "examples/named-entity-recognition", + "examples/summarization", + "examples/standardize-addresses", + "examples/generate-people", + "examples/translation", + "examples/anonymization", + "examples/code-explanation" ] }, { - "group": "Examples", + "group": "Agentic", "pages": [ "examples/rock-paper-scissors", "examples/agent-engineer", diff --git a/examples/pineapple_pizza.py b/examples/pineapple_pizza.py deleted file mode 100644 index 4761f9f3..00000000 --- a/examples/pineapple_pizza.py +++ /dev/null @@ -1,39 +0,0 @@ -from controlflow import Agent, Task, flow - -a1 = Agent( - name="Half-full", - instructions=""" - You are an eternal optimist. - """, -) -a2 = Agent( - name="Half-empty", - instructions=""" - You are an eternal pessimist. - """, -) -# create an agent that will decide who wins the debate -a3 = Agent(name="Moderator") - - -@flow -def demo(): - topic = "pineapple on pizza" - - task = Task( - "Have a debate about the topic. Each agent should take at least two turns.", - agents=[a1, a2], - context={"topic": topic}, - ) - task.run() - - task2 = Task( - "which argument do you find more compelling?", - result_type=[a1.name, a2.name], - agents=[a3], - ) - task2.run() - - -if __name__ == "__main__": - demo() diff --git a/src/controlflow/cli/dev.py b/src/controlflow/cli/dev.py index f0d9b476..6c4ae4e6 100644 --- a/src/controlflow/cli/dev.py +++ b/src/controlflow/cli/dev.py @@ -8,7 +8,7 @@ @dev_app.command() -def generate_ai_files( +def ai_files( output_path: str = typer.Option( ".", "--output", @@ -27,11 +27,6 @@ def generate_ai_files( docs_path = repo_root / "docs" output_dir = Path(output_path).resolve() - typer.echo(f"Repo root: {repo_root}") - typer.echo(f"src_path: {src_path}") - typer.echo(f"docs_path: {docs_path}") - typer.echo(f"output_dir: {output_dir}") - def generate_file_content(file_paths, output_file): with open(output_dir / output_file, "w") as f: for file_path in file_paths: @@ -40,7 +35,11 @@ def generate_file_content(file_paths, output_file): f.write("\n\n") code_files = list(src_path.rglob("*.py")) + list(src_path.rglob("*.jinja")) - doc_files = list(docs_path.rglob("*.mdx")) + list(docs_path.glob("mint.json")) + doc_files = ( + list(docs_path.rglob("*.mdx")) + + list(docs_path.glob("*.md")) + + list(docs_path.glob("mint.json")) + ) generate_file_content(code_files, "all_code.md") generate_file_content(doc_files, "all_docs.md")