Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Dmytro Bobrenko
commited on
Commit
·
65526b0
1
Parent(s):
32f9f6f
Add OpenAPI
Browse files- README.md +6 -1
- middlewares.py +8 -0
- requirements.txt +2 -1
- schemas.py +29 -0
- server.py +44 -8
README.md
CHANGED
@@ -77,7 +77,7 @@ At present, the API provides two endpoints: `/chat` (live) and `/echo` (test).
|
|
77 |
|
78 |
`/chat` is used to chat with the network and receive a response. It requires a JSON payload structured as per the QueryValidatorParams class.
|
79 |
The request payload requires the following parameters encapsulated within the [`QueryValidatorParams`](./validators/base.py) data class:
|
80 |
-
- `
|
81 |
- `exclude: List[str]`: A list of roles or agents to exclude from querying.
|
82 |
- `roles: List[str]`: The roles of the agents to query.
|
83 |
- `messages: List[str]`: The messages to be sent to the network.
|
@@ -124,6 +124,11 @@ Final JSON:
|
|
124 |
```
|
125 |
After verifying that the server is responding to requests locally, you can test the server on a remote machine.
|
126 |
|
|
|
|
|
|
|
|
|
|
|
127 |
### Troubleshooting
|
128 |
|
129 |
If you do not receive a response from the server, check that the server is running and that the port is open on the server. You can open the port using the following commands:
|
|
|
77 |
|
78 |
`/chat` is used to chat with the network and receive a response. It requires a JSON payload structured as per the QueryValidatorParams class.
|
79 |
The request payload requires the following parameters encapsulated within the [`QueryValidatorParams`](./validators/base.py) data class:
|
80 |
+
- `k: int`: The number of miners from which to request responses.
|
81 |
- `exclude: List[str]`: A list of roles or agents to exclude from querying.
|
82 |
- `roles: List[str]`: The roles of the agents to query.
|
83 |
- `messages: List[str]`: The messages to be sent to the network.
|
|
|
124 |
```
|
125 |
After verifying that the server is responding to requests locally, you can test the server on a remote machine.
|
126 |
|
127 |
+
## Accessing OpenAPI Documentation
|
128 |
+
To access OpenAPI documentation, go to:
|
129 |
+
[http://localhost:10000/docs](http://localhost:10000/docs)
|
130 |
+
|
131 |
+
|
132 |
### Troubleshooting
|
133 |
|
134 |
If you do not receive a response from the server, check that the server is running and that the port is open on the server. You can open the port using the following commands:
|
middlewares.py
CHANGED
@@ -8,6 +8,10 @@ EXPECTED_ACCESS_KEY = os.environ.get("EXPECTED_ACCESS_KEY")
|
|
8 |
|
9 |
@middleware
|
10 |
async def api_key_middleware(request: Request, handler):
|
|
|
|
|
|
|
|
|
11 |
# Logging the request
|
12 |
bt.logging.info(f"Handling {request.method} request to {request.path}")
|
13 |
|
@@ -23,6 +27,10 @@ async def api_key_middleware(request: Request, handler):
|
|
23 |
|
24 |
@middleware
|
25 |
async def json_parsing_middleware(request: Request, handler):
|
|
|
|
|
|
|
|
|
26 |
try:
|
27 |
# Parsing JSON data from the request
|
28 |
request["data"] = await request.json()
|
|
|
8 |
|
9 |
@middleware
|
10 |
async def api_key_middleware(request: Request, handler):
|
11 |
+
if request.path.startswith("/docs") or request.path.startswith("/static/swagger"):
|
12 |
+
# Skip checks when accessing OpenAPI documentation.
|
13 |
+
return await handler(request)
|
14 |
+
|
15 |
# Logging the request
|
16 |
bt.logging.info(f"Handling {request.method} request to {request.path}")
|
17 |
|
|
|
27 |
|
28 |
@middleware
|
29 |
async def json_parsing_middleware(request: Request, handler):
|
30 |
+
if request.path.startswith("/docs") or request.path.startswith("/static/swagger"):
|
31 |
+
# Skip checks when accessing OpenAPI documentation.
|
32 |
+
return await handler(request)
|
33 |
+
|
34 |
try:
|
35 |
# Parsing JSON data from the request
|
36 |
request["data"] = await request.json()
|
requirements.txt
CHANGED
@@ -1,3 +1,4 @@
|
|
1 |
git+https://github.com/opentensor/prompting.git@features/move-validator-into-prompting
|
2 |
aiohttp
|
3 |
-
deprecated
|
|
|
|
1 |
git+https://github.com/opentensor/prompting.git@features/move-validator-into-prompting
|
2 |
aiohttp
|
3 |
+
deprecated
|
4 |
+
aiohttp_apispec>=2.3.3
|
schemas.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from marshmallow import Schema, fields
|
2 |
+
|
3 |
+
|
4 |
+
class QueryValidatorParamsSchema(Schema):
|
5 |
+
k = fields.Int(description="The number of miners from which to request responses.")
|
6 |
+
exclude = fields.List(fields.Str(), description="A list of roles or agents to exclude from querying.")
|
7 |
+
roles = fields.List(fields.Str(), required=True, description="The roles of the agents to query.")
|
8 |
+
messages = fields.List(fields.Str(), required=True, description="The messages to be sent to the network.")
|
9 |
+
timeout = fields.Int(description="The time in seconds to wait for a response.")
|
10 |
+
prefer = fields.Str(description="The preferred response format, can be either 'longest' or 'shortest'.")
|
11 |
+
sampling_mode = fields.Str(
|
12 |
+
description="The mode of sampling to use, defaults to 'random'. Can be either 'random' or 'top_incentive'.")
|
13 |
+
|
14 |
+
|
15 |
+
class StreamChunkSchema(Schema):
|
16 |
+
delta = fields.Str(required=True, description="The new chunk of response received.")
|
17 |
+
finish_reason = fields.Str(description="The reason for the response completion, if applicable.")
|
18 |
+
accumulated_chunks = fields.List(fields.Str(), description="All accumulated chunks of responses.")
|
19 |
+
accumulated_chunks_timings = fields.List(fields.Float(), description="Timing for each chunk received.")
|
20 |
+
timestamp = fields.Str(required=True, description="The timestamp at which the chunk was processed.")
|
21 |
+
sequence_number = fields.Int(required=True, description="A sequential identifier for the response part.")
|
22 |
+
selected_uid = fields.Int(required=True, description="The identifier for the selected response source.")
|
23 |
+
|
24 |
+
|
25 |
+
class StreamErrorSchema(Schema):
|
26 |
+
error = fields.Str(required=True, description="Description of the error occurred.")
|
27 |
+
timestamp = fields.Str(required=True, description="The timestamp of the error.")
|
28 |
+
sequence_number = fields.Int(required=True, description="A sequential identifier for the error.")
|
29 |
+
finish_reason = fields.Str(default="error", description="Indicates an error completion.")
|
server.py
CHANGED
@@ -2,14 +2,29 @@ import asyncio
|
|
2 |
import utils
|
3 |
import bittensor as bt
|
4 |
from aiohttp import web
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
from validators import S1ValidatorAPI, QueryValidatorParams, ValidatorAPI
|
6 |
from middlewares import api_key_middleware, json_parsing_middleware
|
7 |
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
async def chat(request: web.Request) -> web.StreamResponse:
|
10 |
-
"""
|
11 |
-
Chat endpoint for the validator.
|
12 |
-
"""
|
13 |
params = QueryValidatorParams.from_request(request)
|
14 |
|
15 |
# Access the validator from the application context
|
@@ -19,6 +34,14 @@ async def chat(request: web.Request) -> web.StreamResponse:
|
|
19 |
return response
|
20 |
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
async def echo_stream(request: web.Request) -> web.StreamResponse:
|
23 |
return await utils.echo_stream(request)
|
24 |
|
@@ -27,19 +50,32 @@ class ValidatorApplication(web.Application):
|
|
27 |
def __init__(self, validator_instance=None, *args, **kwargs):
|
28 |
super().__init__(*args, **kwargs)
|
29 |
|
30 |
-
self["validator"] = (
|
31 |
-
validator_instance if validator_instance else S1ValidatorAPI()
|
32 |
-
)
|
33 |
|
34 |
# Add middlewares to application
|
35 |
-
self.add_routes(
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
self.setup_middlewares()
|
37 |
# TODO: Enable rewarding and other features
|
38 |
|
39 |
def setup_middlewares(self):
|
|
|
40 |
self.middlewares.append(json_parsing_middleware)
|
41 |
self.middlewares.append(api_key_middleware)
|
42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
def main(run_aio_app=True, test=False) -> None:
|
45 |
loop = asyncio.get_event_loop()
|
@@ -48,7 +84,7 @@ def main(run_aio_app=True, test=False) -> None:
|
|
48 |
# Instantiate the application with the actual validator
|
49 |
bt.logging.info("Starting validator application.")
|
50 |
validator_app = ValidatorApplication()
|
51 |
-
bt.logging.success(
|
52 |
|
53 |
try:
|
54 |
web.run_app(validator_app, port=port, loop=loop)
|
|
|
2 |
import utils
|
3 |
import bittensor as bt
|
4 |
from aiohttp import web
|
5 |
+
from aiohttp_apispec import (
|
6 |
+
setup_aiohttp_apispec,
|
7 |
+
validation_middleware,
|
8 |
+
docs,
|
9 |
+
request_schema,
|
10 |
+
response_schema,
|
11 |
+
)
|
12 |
from validators import S1ValidatorAPI, QueryValidatorParams, ValidatorAPI
|
13 |
from middlewares import api_key_middleware, json_parsing_middleware
|
14 |
|
15 |
+
from schemas import QueryValidatorParamsSchema, StreamChunkSchema, StreamErrorSchema
|
16 |
|
17 |
+
|
18 |
+
@docs(
|
19 |
+
tags=["Prompting API"],
|
20 |
+
summary="Chat with the network",
|
21 |
+
description="Chat endpoint for the validator.",
|
22 |
+
)
|
23 |
+
@request_schema(QueryValidatorParamsSchema)
|
24 |
+
@response_schema(StreamChunkSchema, 200)
|
25 |
+
@response_schema(StreamErrorSchema, 400)
|
26 |
async def chat(request: web.Request) -> web.StreamResponse:
|
27 |
+
"""Chat endpoint for the validator"""
|
|
|
|
|
28 |
params = QueryValidatorParams.from_request(request)
|
29 |
|
30 |
# Access the validator from the application context
|
|
|
34 |
return response
|
35 |
|
36 |
|
37 |
+
@docs(
|
38 |
+
tags=["Prompting API"],
|
39 |
+
summary="Echo test",
|
40 |
+
description="Echo endpoint for testing purposes.",
|
41 |
+
)
|
42 |
+
@request_schema(QueryValidatorParamsSchema)
|
43 |
+
@response_schema(StreamChunkSchema, 200)
|
44 |
+
@response_schema(StreamErrorSchema, 400)
|
45 |
async def echo_stream(request: web.Request) -> web.StreamResponse:
|
46 |
return await utils.echo_stream(request)
|
47 |
|
|
|
50 |
def __init__(self, validator_instance=None, *args, **kwargs):
|
51 |
super().__init__(*args, **kwargs)
|
52 |
|
53 |
+
self["validator"] = validator_instance if validator_instance else S1ValidatorAPI()
|
|
|
|
|
54 |
|
55 |
# Add middlewares to application
|
56 |
+
self.add_routes(
|
57 |
+
[
|
58 |
+
web.post("/chat/", chat),
|
59 |
+
web.post("/echo/", echo_stream),
|
60 |
+
]
|
61 |
+
)
|
62 |
+
self.setup_openapi()
|
63 |
self.setup_middlewares()
|
64 |
# TODO: Enable rewarding and other features
|
65 |
|
66 |
def setup_middlewares(self):
|
67 |
+
self.middlewares.append(validation_middleware)
|
68 |
self.middlewares.append(json_parsing_middleware)
|
69 |
self.middlewares.append(api_key_middleware)
|
70 |
|
71 |
+
def setup_openapi(self):
|
72 |
+
setup_aiohttp_apispec(
|
73 |
+
app=self,
|
74 |
+
title="Prompting API",
|
75 |
+
url="/docs/swagger.json",
|
76 |
+
swagger_path="/docs",
|
77 |
+
)
|
78 |
+
|
79 |
|
80 |
def main(run_aio_app=True, test=False) -> None:
|
81 |
loop = asyncio.get_event_loop()
|
|
|
84 |
# Instantiate the application with the actual validator
|
85 |
bt.logging.info("Starting validator application.")
|
86 |
validator_app = ValidatorApplication()
|
87 |
+
bt.logging.success("Validator app initialized successfully", validator_app)
|
88 |
|
89 |
try:
|
90 |
web.run_app(validator_app, port=port, loop=loop)
|