Skip to content
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

add script plus instructions on how to generate API reference #80

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docs/*.md
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
- Pull requests should only be merged once all checks pass
- The repo uses Black for formatting Python code, Prettier for formatting Markdown,
Pyright for type-checking Python, and a few other tools
- To generate reference documentation:
- Add/update docstrings in the codebase. If you are adding a new class/function, add
it's name to `documented_items` in `docs/generate_api_reference.py`
- run `cd docs;python generate_api_reference.py`
- [Internal only] Copy the contents of `api_reference.md` to the reference page in
README.
- To run the CI checks locally:
- `pip install pre-commit`
- `pre-commit run --all` (or `pre-commit install` to install the pre-commit hook)
Expand Down
190 changes: 190 additions & 0 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@


The following it the API reference for the [fastapi_poe](https://github.com/poe-platform/fastapi_poe) client library. The reference assumes that you used `import fastapi as fp`.

## `fp.PoeBot`

The class that you use to define your bot behavior. Once you define your PoeBot class, you
pass it to `make_app` to create a FastAPI app that serves your bot.

#### Parameters:
- `path`: This is the path at which your bot is served. By default, it's set to "/"
but this is something you can adjust. This is especially useful if you want to serve
multiple bots from one server.
- `access_key`: This is the access key for your bot and when provided is used to validate
that the requests are coming from a trusted source. This access key should be the same
one that you provide when integrating your bot with Poe at:
https://poe.com/create_bot?server=1. You can also set this to None but certain features like
file output that mandate an `access_key` will not be available for your bot.
- `concat_attachments_to_message`: A flag to decide whether to parse out content from
attachments and concatenate it to the conversation message. This is set to `True` by default
and we recommend leaving on since it allows your bot to comprehend attachments uploaded by
users by default.

### `PoeBot.get_response`

Override this to define your bot's response given a user query.

### `PoeBot.get_response_with_context`

A version of `get_response` that also includes the request context information. By
default, this will call `get_response`.

### `PoeBot.get_settings`

Override this to define your bot's settings.

### `PoeBot.get_settings_with_context`

A version of `get_settings` that also includes the request context information. By
default, this will call `get_settings`.

### `PoeBot.on_feedback`

Override this to record feedback from the user.

### `PoeBot.on_feedback_with_context`

A version of `on_feedback` that also includes the request context information. By
default, this will call `on_feedback`.

### `PoeBot.on_error`

Override this to record errors from the Poe server.

### `PoeBot.on_error_with_context`

A version of `on_error` that also includes the request context information. By
default, this will call `on_error`.

### `PoeBot.post_message_attachment`

Used to output an attachment in your bot's response.

#### Parameters:
- `message_id`: The message id associated with the current QueryRequest object.
**Important**: This must be the request that is currently being handled by
get_response. Attempting to attach files to previously handled requests will fail.
- `download_url`: A url to the file to be attached to the message.
- `file_data`: The contents of the file to be uploaded. This should be a
bytes-like or file object.
- `filename`: The name of the file to be attached.

**Note**: You need to provide either the `download_url` or both of `file_data` and
`filename`.

### `PoeBot.concat_attachment_content_to_message_body`

Concatenate received attachment file content into the message body. This will be called
by default if `concat_attachments_to_message` is set to `True` but can also be used
manually if needed.



## `fp.make_app`

Create an app object for your bot(s).

#### Parameters:
- `bot`: A bot object or a list of bot objects if you want to host multiple bots on one server.
- `access_key`: The access key to use. If not provided, the server tries to read
the POE_ACCESS_KEY environment variable. If that is not set, the server will
refuse to start, unless `allow_without_key` is True. If multiple bots are provided,
the access key must be provided as part of the bot object.
- `api_key`: The previous name for `access_key`. This is not to be confused with the `api_key`
param needed by `stream_request`. This param is deprecated and will be removed in a future
version.
- `allow_without_key`: If True, the server will start even if no access key is provided.
Requests will not be checked against any key. If an access key is provided, it is still checked.
- `app`: A FastAPI app instance. If provided, the app will be configured with the provided bots,
access keys, and other settings. If not provided, a new FastAPI application instance will be
created and configured.



## `fp.run`

Serve a poe bot using a FastAPI app. This function should be used when you are running the
bot locally. The arguments are the same as they are for `make_app`.



## `fp.stream_request`

The Entry point for the Bot Query API. This API allows you to use other bots on Poe for
inference in response to a user message. For more details, checkout:
https://creator.poe.com/docs/accessing-other-bots-on-poe

#### Parameters:
- `request`: A QueryRequest object representing a query from Poe. This object also includes
information needed to identify the user for compute point usage.
- `bot_name`: The bot you want to invoke.
- `api_key`: Your Poe API key, available at poe.com/api_key. You will need this in case you are
trying to use this function from a script/shell. Note that if an `api_key` is provided,
compute points will be charged on the account corresponding to the `api_key`.



## `fp.get_bot_response`

Use this function to invoke another Poe bot from your shell.
#### Parameters:
- `messages`: A list of protocol messages representing your conversation
- `bot_name`: The bot that you want to invoke.
- `api_key`: Your Poe API key. This is available at: [poe.com/api_key](https://poe.com/api_key)



## `fp.get_final_response`

A helper function for the bot query API that waits for all the tokens and concatenates the full
response before returning.

#### Parameters:
- `request`: A QueryRequest object representing a query from Poe. This object also includes
information needed to identify the user for compute point usage.
- `bot_name`: The bot you want to invoke.
- `api_key`: Your Poe API key, available at poe.com/api_key. You will need this in case you are
trying to use this function from a script/shell. Note that if an `api_key` is provided,
compute points will be charged on the account corresponding to the `api_key`.



## `fp.PartialResponse`

Representation of a (possibly partial) response from a bot. Yield this in
`PoeBot.get_response` or `PoeBot.get_response_with_context` to communicate your response to Poe.

#### Parameters:
- `text`: The actual text you want to display to the user. Note that this should solely
be the text in the next token since Poe will automatically concatenate all tokens before
displaying the response to the user.
- `data`: Used to send arbitrary json data to Poe. This is currently only used for OpenAI
function calling.
- `is_suggested_reply`: Seting this to true will create a suggested reply with the provided
text value.
- `is_replace_response`: Setting this to true will clear out the previously displayed text
to the user and replace it with the provided text value.



## `fp.ErrorResponse`

Similar to `PartialResponse`. Yield this to communicate errors from your bot.

#### Parameters:
- `allow_retry`: Whether or not to allow a user to retry on error.
- `error_type`: An enum indicating what error to display.



## `fp.MetaResponse`

Similar to `Partial Response`. Yield this to communicate `meta` events from server bots.

#### Parameters:
- `suggested_replies`: Whether or not to enable suggested replies.
- `content_type`: Used to describe the format of the response. The currently supported values
are `text/plain` and `text/markdown`.
- `refetch_settings`: Used to trigger a settings fetch request from Poe. A more robust way
to trigger this is documented at: https://creator.poe.com/docs/updating-bot-settings
111 changes: 111 additions & 0 deletions docs/generate_api_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import inspect
import sys
import types
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional, Union

sys.path.append("../src")
import fastapi_poe

INITIAL_TEXT = """

The following it the API reference for the \
[fastapi_poe](https://github.com/poe-platform/fastapi_poe) client library. The reference assumes \
that you used `import fastapi as fp`.

"""


@dataclass
class DocumentationData:
name: str
docstring: Optional[str]
data_type: str
children: List = field(default_factory=lambda: [])


def _unwrap_func(func_obj: Union[staticmethod, Callable]) -> Callable:
"""Grab the underlying func_obj."""
if isinstance(func_obj, staticmethod):
return _unwrap_func(func_obj.__func__)
return func_obj


def get_documentation_data(
*, module: types.ModuleType, documented_items: List[str]
) -> Dict[str, DocumentationData]:
data_dict = {}
for name, obj in inspect.getmembers(module):
if (
inspect.isclass(obj) or inspect.isfunction(obj)
) and name in documented_items:
doc = inspect.getdoc(obj)
data_type = "class" if inspect.isclass(obj) else "function"
dd_obj = DocumentationData(name=name, docstring=doc, data_type=data_type)

if inspect.isclass(obj):
children = []
# for func_name, func_obj in inspect.getmembers(obj, inspect.isfunction):
for func_name, func_obj in obj.__dict__.items():
if not inspect.isfunction(func_obj):
continue
if not func_name.startswith("_"):
func_obj = _unwrap_func(func_obj)
func_doc = inspect.getdoc(func_obj)
children.append(
DocumentationData(
name=func_name, docstring=func_doc, data_type="function"
)
)
dd_obj.children = children
data_dict[name] = dd_obj
return data_dict


def generate_documentation(
*,
data_dict: Dict[str, DocumentationData],
documented_items: List[str],
output_filename: str,
) -> None:
# reset the file first
with open(output_filename, "w") as f:
f.write("")

with open(output_filename, "w") as f:
f.write(INITIAL_TEXT)

for item in documented_items:
item_data = data_dict[item]
f.write(f"## `fp.{item_data.name}`\n\n")
f.write(f"{item_data.docstring}\n\n")
for child in item_data.children:
if not child.docstring:
continue
f.write(f"### `{item}.{child.name}`\n\n")
f.write(f"{child.docstring}\n\n")
f.write("\n\n")


# Specify the names of classes and functions to document
documented_items = [
"PoeBot",
"make_app",
"run",
"stream_request",
"get_bot_response",
"get_final_response",
"PartialResponse",
"ErrorResponse",
"MetaResponse",
]

data_dict = get_documentation_data(
module=fastapi_poe, documented_items=documented_items
)
output_filename = "api_reference.md"
generate_documentation(
data_dict=data_dict,
documented_items=documented_items,
output_filename=output_filename,
)
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ target-version = ['py37']
skip-magic-trailing-comma = true

[tool.ruff]
select = [
lint.select = [
"F",
"E",
"I", # import sorting
Expand Down Expand Up @@ -69,7 +69,7 @@ select = [
"PERF101", # unnecessary list() calls that can be replaced with literals
]

ignore = [
lint.ignore = [
"B008", # do not perform function calls in argument defaults
"ANN101", # missing type annotation for self in method
"ANN102", # missing type annotation for cls in classmethod
Expand Down
Loading
Loading