-
Notifications
You must be signed in to change notification settings - Fork 3
docs: Add documentation for Services and Tasks #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
stefanorosanelli
wants to merge
1
commit into
main
Choose a base branch
from
feat/services-tasks-documentation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,236 @@ | ||||||||
| # Services and Tasks in Brevia | ||||||||
|
|
||||||||
| This document explains the concepts of Services and Tasks in Brevia, how they are implemented, and how they can be used and customized. | ||||||||
|
|
||||||||
| ## Services | ||||||||
|
|
||||||||
| Services in Brevia are high-level components responsible for handling incoming requests, processing them, and returning results. They act as an orchestration layer, often utilizing Tasks to perform specific operations. | ||||||||
|
|
||||||||
| ### `BaseService` | ||||||||
|
|
||||||||
| All services in Brevia should inherit from the `brevia.services.BaseService` abstract class. This class provides a common structure for services. | ||||||||
|
|
||||||||
| The key methods of `BaseService` are: | ||||||||
|
|
||||||||
| * **`run(self, payload: dict) -> dict`**: This is the main entry point for a service. It first calls the `validate` method to check the input `payload`. If validation is successful, it calls the `execute` method to perform the service logic. It returns a dictionary containing the result. | ||||||||
| * **`validate(self, payload: dict) -> bool`**: This abstract method must be implemented by concrete service classes. It's responsible for validating the input `payload` to ensure it contains all necessary data in the correct format. It should return `True` if the payload is valid, and `False` otherwise. | ||||||||
| * **`execute(self, payload: dict) -> dict`**: This abstract method must be implemented by concrete service classes. It contains the core logic of the service. This is where the service might interact with Tasks, databases, or other components to produce a result. It returns a dictionary containing the output of the service. | ||||||||
|
|
||||||||
| ### Creating a Custom Service | ||||||||
|
|
||||||||
| To create a custom service, you need to: | ||||||||
|
|
||||||||
| 1. Define a new class that inherits from `BaseService`. | ||||||||
| 2. Implement the `validate` method to define how the input payload should be checked. | ||||||||
| 3. Implement the `execute` method to define the service's core logic. | ||||||||
|
|
||||||||
| #### Example of a Custom Service | ||||||||
|
|
||||||||
| Here's a simple example of a custom service that takes a name in its payload and returns a greeting: | ||||||||
|
|
||||||||
| ```python | ||||||||
| from brevia.services import BaseService | ||||||||
|
|
||||||||
| class GreetingService(BaseService): | ||||||||
| """A simple service that returns a greeting.""" | ||||||||
|
|
||||||||
| def validate(self, payload: dict) -> bool: | ||||||||
| """Validate that the payload contains a 'name'.""" | ||||||||
| if 'name' not in payload: | ||||||||
| print("Validation failed: 'name' is missing from payload.") | ||||||||
| return False | ||||||||
| if not isinstance(payload['name'], str): | ||||||||
| print("Validation failed: 'name' must be a string.") | ||||||||
| return False | ||||||||
| return True | ||||||||
|
|
||||||||
| def execute(self, payload: dict) -> dict: | ||||||||
| """Execute the service logic to return a greeting.""" | ||||||||
| name = payload['name'] | ||||||||
| greeting = f"Hello, {name}!" | ||||||||
| return {"greeting": greeting} | ||||||||
|
|
||||||||
| # Example usage (typically, a service like this would be called by an API endpoint): | ||||||||
| # if __name__ == "__main__": | ||||||||
| # service = GreetingService() | ||||||||
| # valid_payload = {"name": "World"} | ||||||||
| # invalid_payload_type = {"name": 123} | ||||||||
| # invalid_payload_missing = {} | ||||||||
|
|
||||||||
| # print(f"Running with valid payload: {valid_payload}") | ||||||||
| # result = service.run(valid_payload) | ||||||||
| # print(f"Result: {result}") # Output: {'greeting': 'Hello, World!'} | ||||||||
|
|
||||||||
| # print(f"Running with invalid payload (type error): {invalid_payload_type}") | ||||||||
| # try: | ||||||||
| # service.run(invalid_payload_type) | ||||||||
| # except ValueError as e: | ||||||||
| # print(f"Error: {e}") # This will not be caught here as validate prints and returns False | ||||||||
| # The run method would raise a ValueError if validate returns False | ||||||||
|
|
||||||||
| # print(f"Running with invalid payload (missing key): {invalid_payload_missing}") | ||||||||
| # try: | ||||||||
| # service.run(invalid_payload_missing) | ||||||||
| # except ValueError as e: | ||||||||
| # print(f"Error: {e}") # Similar to above | ||||||||
| ``` | ||||||||
|
|
||||||||
| In a typical Brevia application, services are often invoked by API endpoints (e.g., using FastAPI) which handle the HTTP requests and responses. The service's `run` method would be called with the request data as the payload. | ||||||||
|
|
||||||||
| ## Tasks | ||||||||
|
|
||||||||
| Tasks in Brevia encapsulate the actual logic for performing specific operations, especially those involving data processing, interactions with machine learning models (like those from LangChain), or other detailed computations. Services delegate work to Tasks. | ||||||||
|
|
||||||||
| ### `BaseAnalysisTask` | ||||||||
|
|
||||||||
| Many tasks, especially those performing some form of analysis, inherit from `brevia.tasks.base.BaseAnalysisTask`. This abstract class provides a basic structure for such tasks. | ||||||||
|
|
||||||||
| The key methods of `BaseAnalysisTask` are: | ||||||||
|
|
||||||||
| * **`perform_task(self) -> dict`**: This abstract method must be implemented by concrete task classes. It contains the core logic of the task, such as processing input data, interacting with LLMs, and generating results. It returns a dictionary containing the task's output. | ||||||||
| * **`load_analysis_prompts(self, prompts: dict | None = None)`**: This abstract method is responsible for loading and configuring the prompts that the task will use, often from dictionaries or YAML files. The structure of the `prompts` dictionary can vary based on the task's needs. | ||||||||
|
|
||||||||
| Brevia also provides more specialized base tasks, like `brevia.tasks.text_analysis.BaseTextAnalysisTask`, which can be a more suitable parent for tasks specifically dealing with text. `BaseTextAnalysisTask`, for instance, includes a `text_documents` method to help load and split text into documents. | ||||||||
|
|
||||||||
| ### Interaction with LangChain | ||||||||
|
|
||||||||
| Tasks are often the place where Brevia integrates with LangChain. A task might: | ||||||||
| * Load LangChain LLMs (e.g., using `brevia.models.load_chatmodel`). | ||||||||
| * Define or load LangChain prompt templates. | ||||||||
| * Construct and run LangChain chains (e.g., `load_summarize_chain`, custom `LLMChain` instances, or `SequentialChain`). | ||||||||
| * Process the inputs for and outputs from these chains. | ||||||||
|
|
||||||||
| ### Creating a Custom Task | ||||||||
|
|
||||||||
| To create a custom task, you generally need to: | ||||||||
|
|
||||||||
| 1. Define a new class that inherits from `BaseAnalysisTask` or another suitable base task class (e.g., `BaseTextAnalysisTask`). | ||||||||
| 2. Implement the `load_analysis_prompts` method if your task uses configurable prompts. | ||||||||
| 3. Implement the `perform_task` method to define the task's core logic. This might involve initializing models, preparing data, running chains, and formatting the output. | ||||||||
|
|
||||||||
| #### Example of a Custom Task | ||||||||
|
|
||||||||
| Here's a conceptual example of a custom task that processes some text. (Note: This is a simplified example. Real tasks often involve more complex setup, especially when using LLMs.) | ||||||||
|
|
||||||||
| ```python | ||||||||
| from brevia.tasks.base import BaseAnalysisTask | ||||||||
| from brevia.models import load_chatmodel # For a more complex task | ||||||||
| from langchain_core.prompts import PromptTemplate | ||||||||
| from langchain.chains.llm import LLMChain | ||||||||
|
|
||||||||
| # Assume a simple prompt for this example | ||||||||
| DEFAULT_PROMPT_TEMPLATE = "Analyze the following text: {text_input}. What is its main topic?" | ||||||||
|
|
||||||||
| class SimpleTextAnalysisTask(BaseAnalysisTask): | ||||||||
| """A simple task to analyze a piece of text.""" | ||||||||
|
|
||||||||
| def __init__(self, text: str, custom_prompt_template: str | None = None): | ||||||||
|
||||||||
| def __init__(self, text: str, custom_prompt_template: str | None = None): | |
| def __init__(self, text: str, custom_prompt_template: str | None = None): | |
| super().__init__() # Ensure proper initialization of the base class |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment contradicts the earlier note about validation printing and returning False. Clarify whether
runraises aValueErroron validation failure and update the example comments to reflect the actual behavior.