Add remote calls
This commit is contained in:
parent
c36d1f7f2a
commit
b5ec37e03c
10 changed files with 217 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ __pycache__
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
build
|
||||||
|
|
|
||||||
90
docs/remote-test/remote.ipynb
Normal file
90
docs/remote-test/remote.ipynb
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Trace(trace_id='07719c28-e248-43f8-a652-608c49ad5a17', created='2022-06-26T07:55:30.587753', original_execution_time_ms=178.268, logged_values={'arg:text:value': 'I love chemical compounds and nuclear fission.', 'arg:text:length': 46, 'arg:target_confidence:value': 50}, models=[Model(key='small-domain-prediction', version=0)], exception=None, output={'labels': [{'label': 'Physics', 'confidence': 28.0, 'explanation': ['nuclear', 'chemical', 'fission', 'compounds', 'love']}, {'label': 'Chemistry', 'confidence': 22.0, 'explanation': ['chemical', 'compounds', 'nuclear', 'fission', 'love']}]}, feedback=None, tags=['predict_domain', 'online', 'development'])"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from great_ai import call_remote_great_ai_async\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"await call_remote_great_ai_async(\n",
|
||||||
|
" \"http://localhost:6060\", {\"text\": \"I love chemical compounds and nuclear fission.\"}\n",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"<_UnixSelectorEventLoop running=True closed=False debug=False>\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ename": "Exception",
|
||||||
|
"evalue": "Already running in an event loop, you have to call call_remote_great_ai_async.",
|
||||||
|
"output_type": "error",
|
||||||
|
"traceback": [
|
||||||
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||||
|
"\u001b[0;31mException\u001b[0m Traceback (most recent call last)",
|
||||||
|
"\u001b[1;32m/data/projects/great_ai/docs/remote-test/remote.ipynb Cell 1'\u001b[0m in \u001b[0;36m<cell line: 4>\u001b[0;34m()\u001b[0m\n\u001b[1;32m <a href='vscode-notebook-cell:/data/projects/great_ai/docs/remote-test/remote.ipynb#ch0000000?line=0'>1</a>\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39mgreat_ai\u001b[39;00m \u001b[39mimport\u001b[39;00m call_remote_great_ai\n\u001b[0;32m----> <a href='vscode-notebook-cell:/data/projects/great_ai/docs/remote-test/remote.ipynb#ch0000000?line=3'>4</a>\u001b[0m call_remote_great_ai(\u001b[39m'\u001b[39;49m\u001b[39mhttp://localhost:6060\u001b[39;49m\u001b[39m'\u001b[39;49m, {\n\u001b[1;32m <a href='vscode-notebook-cell:/data/projects/great_ai/docs/remote-test/remote.ipynb#ch0000000?line=4'>5</a>\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mtext\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39mI love chemical compounds and nuclear fission.\u001b[39;49m\u001b[39m'\u001b[39;49m\n\u001b[1;32m <a href='vscode-notebook-cell:/data/projects/great_ai/docs/remote-test/remote.ipynb#ch0000000?line=5'>6</a>\u001b[0m })\n",
|
||||||
|
"File \u001b[0;32m/data/projects/great_ai/src/great_ai/great_ai/remote/call_remote_great_ai.py:17\u001b[0m, in \u001b[0;36mcall_remote_great_ai\u001b[0;34m(base_uri, data, retry_count)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m 16\u001b[0m \u001b[39mprint\u001b[39m(asyncio\u001b[39m.\u001b[39mget_running_loop())\n\u001b[0;32m---> 17\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mException\u001b[39;00m(\u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39mAlready running in an event loop, you have to call \u001b[39m\u001b[39m{\u001b[39;00mcall_remote_great_ai_async\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m\u001b[39m}\u001b[39;00m\u001b[39m.\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m 18\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m:\n\u001b[1;32m 19\u001b[0m \u001b[39mpass\u001b[39;00m\n",
|
||||||
|
"\u001b[0;31mException\u001b[0m: Already running in an event loop, you have to call call_remote_great_ai_async."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from great_ai import call_remote_great_ai\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"call_remote_great_ai(\n",
|
||||||
|
" \"http://localhost:6060\", {\"text\": \"I love chemical compounds and nuclear fission.\"}\n",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3.10.4 ('.env': venv)",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.10.4"
|
||||||
|
},
|
||||||
|
"orig_nbformat": 4,
|
||||||
|
"vscode": {
|
||||||
|
"interpreter": {
|
||||||
|
"hash": "d88b0dd276e3f918f7798b7e97af2e3c2f843817b9e5b55a9df0a682ffd80f44"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = great-ai
|
name = great-ai
|
||||||
version = 0.0.1
|
version = 0.0.1-rc1
|
||||||
author = András Schmelczer
|
author = András Schmelczer
|
||||||
author_email = andras@scoutinscience.com
|
author_email = andras@scoutinscience.com
|
||||||
description =
|
description =
|
||||||
|
|
@ -44,6 +44,7 @@ install_requires =
|
||||||
uvicorn[standard] >= 0.18.0
|
uvicorn[standard] >= 0.18.0
|
||||||
watchdog >= 2.1.0
|
watchdog >= 2.1.0
|
||||||
pymongo >= 4.0.0
|
pymongo >= 4.0.0
|
||||||
|
aiohttp >= 3.8.0
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
* = *.json, *.yaml, *.yml, *.css
|
* = *.json, *.yaml, *.yml, *.css
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,10 @@ from .output_models import (
|
||||||
)
|
)
|
||||||
from .parameters import log_metric, parameter
|
from .parameters import log_metric, parameter
|
||||||
from .persistence import MongodbDriver, ParallelTinyDbDriver, TracingDatabaseDriver
|
from .persistence import MongodbDriver, ParallelTinyDbDriver, TracingDatabaseDriver
|
||||||
|
from .remote import (
|
||||||
|
HttpClient,
|
||||||
|
RemoteCallError,
|
||||||
|
call_remote_great_ai,
|
||||||
|
call_remote_great_ai_async,
|
||||||
|
)
|
||||||
from .tracing import add_ground_truth, delete_ground_truth, query_ground_truth
|
from .tracing import add_ground_truth, delete_ground_truth, query_ground_truth
|
||||||
|
|
|
||||||
4
src/great_ai/great_ai/remote/__init__.py
Normal file
4
src/great_ai/great_ai/remote/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .call_remote_great_ai import call_remote_great_ai
|
||||||
|
from .call_remote_great_ai_async import call_remote_great_ai_async
|
||||||
|
from .http_client import HttpClient
|
||||||
|
from .remote_call_error import RemoteCallError
|
||||||
26
src/great_ai/great_ai/remote/call_remote_great_ai.py
Normal file
26
src/great_ai/great_ai/remote/call_remote_great_ai.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import asyncio
|
||||||
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
from ...utilities import get_logger
|
||||||
|
from ..views import Trace
|
||||||
|
from .call_remote_great_ai_async import call_remote_great_ai_async
|
||||||
|
|
||||||
|
logger = get_logger("call_remote_great_ai")
|
||||||
|
|
||||||
|
|
||||||
|
def call_remote_great_ai(
|
||||||
|
base_uri: str, data: Mapping[str, Any], retry_count: int = 4
|
||||||
|
) -> Trace:
|
||||||
|
try:
|
||||||
|
asyncio.get_running_loop()
|
||||||
|
raise Exception(
|
||||||
|
f"Already running in an event loop, you have to call `{call_remote_great_ai_async.__name__}`"
|
||||||
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
future = call_remote_great_ai_async(
|
||||||
|
base_uri=base_uri, data=data, retry_count=retry_count
|
||||||
|
)
|
||||||
|
|
||||||
|
return asyncio.run(future)
|
||||||
25
src/great_ai/great_ai/remote/call_remote_great_ai_async.py
Normal file
25
src/great_ai/great_ai/remote/call_remote_great_ai_async.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
from typing import Any, Mapping, Optional
|
||||||
|
|
||||||
|
from ..views import Trace
|
||||||
|
from .http_client import HttpClient
|
||||||
|
from .remote_call_error import RemoteCallError
|
||||||
|
|
||||||
|
http: Optional[HttpClient] = None
|
||||||
|
|
||||||
|
|
||||||
|
async def call_remote_great_ai_async(
|
||||||
|
base_uri: str, data: Mapping[str, Any], retry_count: int = 4
|
||||||
|
) -> Trace:
|
||||||
|
global http
|
||||||
|
if http is None:
|
||||||
|
http = HttpClient()
|
||||||
|
|
||||||
|
url = f"{base_uri}/predict/"
|
||||||
|
response = await http.post(
|
||||||
|
url=url, data=data, retry_count=retry_count, expected_status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return Trace.parse_obj(response)
|
||||||
|
except Exception:
|
||||||
|
raise RemoteCallError("Could not parse response")
|
||||||
60
src/great_ai/great_ai/remote/http_client.py
Normal file
60
src/great_ai/great_ai/remote/http_client.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import logging
|
||||||
|
from asyncio import sleep
|
||||||
|
from typing import Any, Mapping, Optional
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
from .remote_call_error import RemoteCallError
|
||||||
|
|
||||||
|
logger = logging.getLogger("http")
|
||||||
|
|
||||||
|
|
||||||
|
class HttpClient:
|
||||||
|
timeout_seconds: int = 600
|
||||||
|
wait_between_retries_seconds: float = 5
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
) -> None:
|
||||||
|
timeout = aiohttp.ClientTimeout(total=self.timeout_seconds)
|
||||||
|
|
||||||
|
self._session = aiohttp.ClientSession(
|
||||||
|
raise_for_status=False,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def post(
|
||||||
|
self,
|
||||||
|
url: str,
|
||||||
|
data: Mapping[str, Any],
|
||||||
|
retry_count: int = 0,
|
||||||
|
expected_status: Optional[int] = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
for i in range(retry_count + 1):
|
||||||
|
try:
|
||||||
|
async with self._session.post(url, json=data, **kwargs) as r:
|
||||||
|
if (
|
||||||
|
expected_status is not None and r.status != expected_status
|
||||||
|
) or r.status >= 500:
|
||||||
|
response_text = await r.text()
|
||||||
|
raise ValueError(
|
||||||
|
f"Found not-expected status code: {r.status}, response is: {response_text}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
return await r.json()
|
||||||
|
except Exception:
|
||||||
|
raise RemoteCallError(
|
||||||
|
"JSON parsing failed",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
f"Request failed ({e}), {retry_count - i - 1} retries left",
|
||||||
|
)
|
||||||
|
if retry_count - i > 1:
|
||||||
|
await sleep(self.wait_between_retries_seconds)
|
||||||
|
|
||||||
|
raise RemoteCallError("Request has failed too many times")
|
||||||
|
|
||||||
|
async def close(self) -> None:
|
||||||
|
await self._session.close()
|
||||||
2
src/great_ai/great_ai/remote/remote_call_error.py
Normal file
2
src/great_ai/great_ai/remote/remote_call_error.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
class RemoteCallError(Exception):
|
||||||
|
pass
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from .clean import clean
|
from .clean import clean
|
||||||
from .config_file import ConfigFile
|
from .config_file import ConfigFile, ParseError
|
||||||
from .evaluate_ranking import evaluate_ranking
|
from .evaluate_ranking import evaluate_ranking
|
||||||
from .get_sentences import get_sentences
|
from .get_sentences import get_sentences
|
||||||
from .language import english_name_of_language, is_english, predict_language
|
from .language import english_name_of_language, is_english, predict_language
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue