Skip to content

Commit

Permalink
Merge pull request #716 from mmikita95/feat-ai-tools
Browse files Browse the repository at this point in the history
feat: `Tools` resource for AI module
  • Loading branch information
ramedina86 authored Jan 7, 2025
2 parents a50852c + 9db0a0d commit 4b9924b
Show file tree
Hide file tree
Showing 3 changed files with 688 additions and 507 deletions.
83 changes: 82 additions & 1 deletion docs/framework/ai-module.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ Function tools require the following properties:

When a conversation involves a tool (either a graph or a function), Framework automatically handles the requests from LLM to use the tools during interactions. If the tool needs multiple steps (for example, querying data and processing it), Framework will handle those steps recursively, calling functions as needed until the final result is returned.

By default, to prevent endless recursion, Framework will only handle 3 consecutive tool calls. You can expand it in case it doesn't suit your case – both `complete()` and `stream_complete()` accept a `max_tool_depth` parameter, which configures the maximum allowed recursion depth:
By default, to prevent endless recursion, Framework will only handle 5 consecutive tool calls. You can expand it in case it doesn't suit your case – both `complete()` and `stream_complete()` accept a `max_tool_depth` parameter, which configures the maximum allowed recursion depth:

```python
response = conversation.complete(tools=tool, max_tool_depth=7)
Expand Down Expand Up @@ -340,3 +340,84 @@ for chunk in stream_ask(
```
</CodeGroup>

## Using the `Tools` class

<Note>
This document outlines the use of `Tools` in the Writer Framework; for more thorough documentation, check out (this guide)[https://dev.writer.com/api-guides/tools].
</Note>

The `writer.ai.tools` instance provides access to Writer SDK `tools` resources, such as text splitting, medical content comprehension, and PDF parsing. Below is a guide on how to use each method.

### Splitting Content

The `split` method divides text into chunks based on a selected strategy.

```python
from writer.ai import tools

content = \
"""
This is a long piece of text that needs to be split into smaller parts.
Lorem ipsum dolor sit amet...
"""
chunks = tools.split(content, strategy="llm_split")
print(chunks)
```

**Parameters**:
- `content` (str): The text to be split.
- `strategy` (str): The splitting strategy (`llm_split`, `fast_split`, or `hybrid_split`).

**Returns**:
A list of text chunks.

### Medical Content Comprehension

The `comprehend_medical` method processes medical text and extracts relevant entities based on a specified response type.

```python
from writer.ai import tools

medical_text = "Patient shows symptoms of hypertension and diabetes."
entities = tools.comprehend_medical(medical_text, response_type="Entities")
print(entities)
```

**Parameters**:
- `content` (str): The medical text to process.
- `response_type` (str): The type of medical response (`Entities`, `RxNorm`, `ICD-10-CM`, or `SNOMED CT`).

**Returns**:
A list of extracted medical entities.

### PDF Parsing

The `parse_pdf` method extracts text content from a PDF file. The file can be referenced by its ID, or provided as a `File` object.

<CodeGroup>
```python file_id
from writer.ai import tools

file_id = "example-file-id"
parsed_content = tools.parse_pdf(file_id, format="text")
print(parsed_content)
```
```python upload_file
from writer.ai import tools

file_object = upload_file(
data=pdf_content,
type="application/pdf",
name="uploaded_file.pdf"
)
parsed_content = tools.parse_pdf(file_object, format="text")
print(parsed_content)
```
</CodeGroup>

**Parameters**:
- `file_id_or_file` (str or File): The file to parse (by ID or as an object).
- `format` (str): The format of the extracted content (`text` or `markdown`).

**Returns**:
The text content of the PDF.
105 changes: 105 additions & 0 deletions src/writer/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,110 @@ def generate_content(
)


class Tools:
SplittingStrategy = Union[
Literal["llm_split"],
Literal["fast_split"],
Literal["hybrid_split"]
]
MedicalResponseType = Union[
Literal["Entities"],
Literal["RxNorm"],
Literal["ICD-10-CM"],
Literal["SNOMED CT"]
]

@staticmethod
def _retrieve_tools_accessor():
return WriterAIManager.acquire_client().tools

@classmethod
def parse_pdf(
cls,
file_id_or_file: Union[str, File, Dict],
format: Union[Literal['text'], Literal['markdown']] = 'text',
config: Optional[APIOptions] = None
) -> str:
config = config or {}
client_tools = cls._retrieve_tools_accessor()
file_id = None
if isinstance(file_id_or_file, File):
file_id = file_id_or_file.id
elif isinstance(file_id_or_file, Dict):
if not (
"data" in file_id_or_file
and
"type" in file_id_or_file
and
"name" in file_id_or_file
):
raise ValueError(
"Invalid payload passed to parse_pdf method: " +
"expected keys `data`, `type` and `name`, " +
f"got {file_id_or_file.keys()}"
)
new_file_from_payload = upload_file(
**file_id_or_file,
config=config
)
file_id = new_file_from_payload.id
elif isinstance(file_id_or_file, str):
file_id = file_id_or_file
else:
raise ValueError(
"parse_pdf expects a `writer.ai.File` type instance, " +
f"a file payload, or a string ID: got {type(file_id_or_file)}"
)

result = client_tools.parse_pdf(
file_id=file_id,
format=format,
**config
)

return result.content

@classmethod
def split(
cls,
content: str,
strategy: SplittingStrategy = "llm_split",
config: Optional[APIOptions] = None
) -> List[str]:
if not content:
raise ValueError("Content cannot be empty.")
config = config or {}
client_tools = cls._retrieve_tools_accessor()

result = client_tools.context_aware_splitting(
strategy=strategy,
text=content,
**config
)

return result.chunks

@classmethod
def comprehend_medical(
cls,
content: str,
response_type: MedicalResponseType = "Entities",
config: Optional[APIOptions] = None
) -> List:
if not content:
raise ValueError("Content cannot be empty.")
config = config or {}
client_tools = cls._retrieve_tools_accessor()

result = client_tools.comprehend.medical(
content=content,
response_type=response_type,
**config
)

return result.entities


def complete(
initial_text: str,
config: Optional['CreateOptions'] = None
Expand Down Expand Up @@ -2339,3 +2443,4 @@ def init(token: Optional[str] = None):


apps = Apps()
tools = Tools()
Loading

0 comments on commit 4b9924b

Please sign in to comment.