Skip to content
This repository has been archived by the owner on Nov 13, 2024. It is now read-only.

canopy server documentation #136

Merged
merged 35 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7ed9291
canopy server documentation
miararoy Nov 2, 2023
bdea5a6
fix response type in Chat
miararoy Nov 2, 2023
78e2238
Merge branch 'dev' into server-docstrings
miararoy Nov 2, 2023
001bac8
ignore line mypy
miararoy Nov 2, 2023
78a0a3b
remove soon dep. flag
miararoy Nov 2, 2023
2ad5385
Add 'ignored' for model param
miararoy Nov 2, 2023
575301c
stream descp
miararoy Nov 2, 2023
257130c
user descp
miararoy Nov 2, 2023
553661c
batch size
miararoy Nov 2, 2023
d4a725e
app.py
miararoy Nov 2, 2023
c7a014c
fix trailing whitespace
miararoy Nov 2, 2023
1d9d566
fix
miararoy Nov 2, 2023
acc42c3
Apply suggestions from code review
miararoy Nov 2, 2023
8227d84
[app] Don't use private attribute
igiloh-pinecone Nov 5, 2023
6f4b0a1
[app] Parent process - use an even better solution
igiloh-pinecone Nov 5, 2023
9db8c0f
[app] Properly handle None case
igiloh-pinecone Nov 5, 2023
8f019b6
[models] Rename ContextContentResponse
igiloh-pinecone Nov 5, 2023
08a7f23
[app] Fix description
igiloh-pinecone Nov 5, 2023
3d16600
Apply Byron's and Nathan's suggestions from code review
igiloh-pinecone Nov 5, 2023
2bbba59
[app] Made generating the docs optional
igiloh-pinecone Nov 5, 2023
4ac9ff9
[app] Moved docs template to their own dedicated file
igiloh-pinecone Nov 5, 2023
71c7fc6
fix linter issues
igiloh-pinecone Nov 5, 2023
3e63f95
[CLI] Control commands order in help message
igiloh-pinecone Nov 5, 2023
3f8acd2
[cli] Rename 'service' to 'server'
igiloh-pinecone Nov 5, 2023
1a29f3a
linter
igiloh-pinecone Nov 5, 2023
3582401
Merge branch 'dev' into server-docstrings
igiloh-pinecone Nov 5, 2023
48c8d1b
Context must contain a ContextContent that implements to_text()
igiloh-pinecone Nov 5, 2023
457b61b
linters
igiloh-pinecone Nov 5, 2023
50efc68
[context] Simplify ContextContent
igiloh-pinecone Nov 6, 2023
f0b40e9
[context] StuffingContextContent - Removed special iterator functions
igiloh-pinecone Nov 6, 2023
4e7a2ce
Merge pull request #140 from pinecone-io/cli_server
igiloh-pinecone Nov 6, 2023
02637da
[context] Moved SuffingContextBuilder's data models into the same file
igiloh-pinecone Nov 6, 2023
db9ca51
[app] `/query` return type - added ContextResponse model
igiloh-pinecone Nov 6, 2023
3c828c4
Linter fixes + wrong return type
igiloh-pinecone Nov 6, 2023
2104b65
Merge pull request #144 from pinecone-io/refactor_context_content
igiloh-pinecone Nov 6, 2023
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,5 @@ cython_debug/
# Mac OS
**/.DS_Store

datafiles/*
datafiles/*
canopy-api-docs.html
4 changes: 2 additions & 2 deletions src/canopy/context_engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

from pydantic import BaseModel

from canopy.models.data_models import ContextContent
from canopy.models.data_models import _ContextContent


class ContextSnippet(BaseModel):
source: str
text: str


class ContextQueryResult(ContextContent):
class ContextQueryResult(_ContextContent):
query: str
snippets: List[ContextSnippet]

Expand Down
2 changes: 1 addition & 1 deletion src/canopy/models/api_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def calc_total_tokens(cls, v, values, **kwargs):


class ChatResponse(BaseModel):
id: str
id: str = Field(description="Canopy session Id.")
object: str
created: int
model: str
Expand Down
64 changes: 42 additions & 22 deletions src/canopy/models/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,72 @@


class Query(BaseModel):
text: str
namespace: str = ""
metadata_filter: Optional[dict] = None
top_k: Optional[int] = None
query_params: dict = Field(default_factory=dict)
text: str = Field(description="The query text.")
namespace: str = Field(
default="",
description="The namespace of the query. To learn more about namespaces, see https://docs.pinecone.io/docs/namespaces", # noqa: E501
)
metadata_filter: Optional[dict] = Field(
default=None,
description="A Pinecone metadata filter, to learn more about metadata filters, see https://docs.pinecone.io/docs/metadata-filtering", # noqa: E501
)
top_k: Optional[int] = Field(
default=None,
description="The number of results to return."
)
query_params: dict = Field(
default_factory=dict,
description="Pinecone Client additional query parameters."
)


class Document(BaseModel):
id: str
text: str
source: str = ""
metadata: Metadata = Field(default_factory=dict)
id: str = Field(description="The document id.")
text: str = Field(description="The document text.")
source: str = Field(
default="",
description="The source of the document: a URL, a file path, etc."
)
metadata: Metadata = Field(
default_factory=dict,
description="The document metadata. To learn more about metadata, see https://docs.pinecone.io/docs/manage-data", # noqa: E501
)

class Config:
extra = Extra.forbid

@validator('metadata')
@validator("metadata")
def metadata_reseved_fields(cls, v):
if 'text' in v:
if "text" in v:
raise ValueError('Metadata cannot contain reserved field "text"')
if 'document_id' in v:
if "document_id" in v:
raise ValueError('Metadata cannot contain reserved field "document_id"')
if 'source' in v:
if "source" in v:
raise ValueError('Metadata cannot contain reserved field "source"')
return v


class ContextContent(BaseModel, ABC):

class _ContextContent(BaseModel, ABC):
# Any context should be able to be represented as well formatted text.
# In the most minimal case, that could simply be a call to `.json()`.
@abstractmethod
def to_text(self, **kwargs) -> str:
pass


ContextContent = Union[_ContextContent, Sequence[_ContextContent]]


class Context(BaseModel):
content: Union[ContextContent, Sequence[ContextContent]]
content: ContextContent
num_tokens: int = Field(exclude=True)
debug_info: dict = Field(default_factory=dict, exclude=True)

def to_text(self, **kwargs) -> str:
if isinstance(self.content, ContextContent):
return self.content.to_text(**kwargs)
else:
if isinstance(self.content, Sequence):
return "\n".join([c.to_text(**kwargs) for c in self.content])
else:
return self.content.to_text(**kwargs)


# --------------------- LLM models ------------------------
Expand All @@ -69,12 +89,12 @@ class Role(Enum):


class MessageBase(BaseModel):
role: Role
content: str
role: Role = Field(description="The role of the message's author. Can be one of ['User', 'Assistant', 'System']")
content: str = Field(description="The contents of the message.")

def dict(self, *args, **kwargs):
d = super().dict(*args, **kwargs)
d['role'] = d['role'].value
d["role"] = d["role"].value
return d


Expand Down
1 change: 1 addition & 0 deletions src/canopy_cli/__init__.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file definitely doesn't belong in the canopy_cli directory (package).

Usually, I would put this file somewhere under the repo's root (not inside src) and use it only in CI. But I get it that you want users to be able to build the documentation locally.
In that case - I'm not even sure this file will be included in the .whl without explicitly being included in the pyproject.toml.
Please check what is the best practice for allowing user's to get documentation locally.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

38 changes: 38 additions & 0 deletions src/canopy_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,5 +569,43 @@ def stop(url):
raise CLIError(msg)


@cli.command(
help=(
"""
\b
Open the Canopy Server docs
"""
)
)
@click.option("--url", default="http://0.0.0.0:8000",
help="Canopy's service url. Defaults to http://0.0.0.0:8000")
def api_docs(url):
import webbrowser

generated_docs = False
try:
check_service_health(url)
except CLIError:
msg = (f"Canopy server is not running. Would you like to generate the docs "
f"to a local HTML file?")
click.confirm(click.style(msg, fg="red"), abort=True)
generated_docs = True

if generated_docs:
import json
from canopy_server._redocs_template import HTML_TEMPLATE
from canopy_server.app import app
# generate docs

filename = "canopy-api-docs.html"
msg = f"Generating docs to {filename}"
click.echo(click.style(msg, fg="green"))
with open(filename, "w") as fd:
print(HTML_TEMPLATE % json.dumps(app.openapi()), file=fd)
webbrowser.open('file://' + os.path.realpath(filename))
else:
webbrowser.open('http://localhost:8000/redoc')


if __name__ == "__main__":
cli()
1 change: 1 addition & 0 deletions src/canopy_server/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

27 changes: 27 additions & 0 deletions src/canopy_server/_redocs_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
HTML_TEMPLATE = """<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Canopy API Spec</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="https://polybit-apps.s3.amazonaws.com/stdlib/users/pinecone/profile/image.png">
<style>
body {
margin: 0;
padding: 0;
}
</style>
<style data-styled="" data-styled-version="4.4.1"></style>
</head>
<body>
<div id="redoc-container"></div>
<title>Redoc</title>
<script src="https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js"> </script>
<script>
var spec = %s;
Redoc.init(spec, {}, document.getElementById("redoc-container"));
</script>
</body>
</html>
""" # noqa: E501
52 changes: 44 additions & 8 deletions src/canopy_server/api_models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
from typing import Optional, List

from pydantic import BaseModel
from pydantic import BaseModel, Field

from canopy.models.data_models import Messages, Query, Document


class ChatRequest(BaseModel):
model: str = ""
messages: Messages
stream: bool = False
user: Optional[str] = None
model: str = Field(
default="",
description="The ID of the model to use. This field is ignored; instead, configure this field in the Canopy config.", # noqa: E501
)
messages: Messages = Field(
description="A list of messages comprising the conversation so far."
)
stream: bool = Field(
default=False,
description="""Whether or not to stream the chatbot's response. If set, the response is server-sent events containing [chat.completion.chunk](https://platform.openai.com/docs/api-reference/chat/streaming) objects""", # noqa: E501
)
user: Optional[str] = Field(
default=None,
description="A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. Unused, reserved for future extensions", # noqa: E501
)


class ContextQueryRequest(BaseModel):
Expand All @@ -19,11 +30,13 @@ class ContextQueryRequest(BaseModel):

class ContextUpsertRequest(BaseModel):
documents: List[Document]
batch_size: int = 200
batch_size: int = Field(
default=200, description="The batch size to use when uploading documents chunks to the Pinecone Index." # noqa: E501
)


class ContextDeleteRequest(BaseModel):
document_ids: List[str]
document_ids: List[str] = Field(description="List of document IDs to delete.")


class HealthStatus(BaseModel):
Expand All @@ -38,5 +51,28 @@ class ChatDebugInfo(BaseModel):
prompt_tokens: Optional[int] = None
generated_tokens: Optional[int] = None

def to_text(self,):
def to_text(
self,
):
return self.json()


class ShutdownResponse(BaseModel):
message: str = Field(
default="Shutting down",
description="Message indicating the server is shutting down.",
)


class SuccessUpsertResponse(BaseModel):
message: str = Field(
default="Success",
description="Message indicating the upsert was successful.",
)


class SuccessDeleteResponse(BaseModel):
message: str = Field(
default="Success",
description="Message indicating the delete was successful.",
)
Loading
Loading