From 041eefc401db8a5810fa24938faac72dfc56782d Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Wed, 13 Jul 2022 20:23:56 +0200 Subject: [PATCH] Fix issues from @angyaloliver's feedback --- README.md | 8 ++++---- docs/examples/scibert.md | 2 +- docs/examples/simple/deploy.ipynb | 2 +- docs/how-to-guides/call-remote.md | 1 + docs/how-to-guides/create-service.md | 6 +++--- docs/how-to-guides/use-service.md | 2 +- docs/index.md | 8 ++++---- docs/tutorial/deploy.ipynb | 2 +- docs/tutorial/train.ipynb | 2 +- great_ai/parameters/parameter.py | 13 ++++++++----- tests/test_async_starters.py | 6 +++--- tests/test_basic_starters.py | 4 ++-- 12 files changed, 30 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3ad78d0..8f146a9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ def greeter(name: str) -> str: Start it by executing `great-ai demo.py`, find the dashboard at [http://localhost:6060](http://localhost:6060/dashboard). -![demo screen capture](docs/media/demo.gif) +![demo screen capture](https://github.com/schmelczer/great-ai/blob/main/docs/media/demo.gif) That's it. Your GreatAI service is *nearly* ready for production use. Many of the [SE4ML best-practices](https://se-ml.github.io) are configured and implemented automatically (of course, these can be customised as well). @@ -48,13 +48,13 @@ GreatAI fits between the prototype and deployment phases of your AI development ## Why GreatAI? -There are other, existing solutions aiming to facilitate this phase. [Amazon SageMaker](https://aws.amazon.com/sagemaker) and [Seldon Core](https://www.seldon.io/solutions/open-source-projects/core) provide the most comprehensive suite of features. If you have the opportunity use those, do that because they're great. +There are other, existing solutions aiming to facilitate this phase. [Amazon SageMaker](https://aws.amazon.com/sagemaker) and [Seldon Core](https://www.seldon.io/solutions/open-source-projects/core) provide the most comprehensive suite of features. If you have the opportunity to use them, do that because they're great. However, [research indicates](https://great-ai.scoutinscience.com) that professionals rarely use them. This may be due to their inherent setup and operating complexity. **GreatAI is designed to be as simple to use as possible.** Its clear, high-level API and sensible default configuration makes it extremely easy to start using. Despite its relative simplicity over Seldon Core, it still implements many of the [SE4ML best-practices](https://se-ml.github.io), and thus, can meaningfully improve your deployment without requiring prohibitively large effort. -## Learn more +## Find `great-ai` on [PyPI](https://hub.docker.com/repository/docker/schmelczera/great-ai) -[Check out the documentation](https://great-ai.scoutinscience.com). +## [Learn more](https://great-ai.scoutinscience.com) ## Find `great-ai` on [DockerHub](https://hub.docker.com/repository/docker/schmelczera/great-ai) diff --git a/docs/examples/scibert.md b/docs/examples/scibert.md index 1a62881..90af250 100644 --- a/docs/examples/scibert.md +++ b/docs/examples/scibert.md @@ -3,5 +3,5 @@ This example shows how `great-ai` is used in practice at ScoutinScience.
-[:material-test-tube: Check out the code](/docs/examples/scibert){ .md-button .md-button--primary } +[:material-test-tube: Check out the code](https://github.com/schmelczer/great-ai/tree/main/docs/examples/scibert){ .md-button .md-button--primary }
diff --git a/docs/examples/simple/deploy.ipynb b/docs/examples/simple/deploy.ipynb index e27cc23..d5072ab 100644 --- a/docs/examples/simple/deploy.ipynb +++ b/docs/examples/simple/deploy.ipynb @@ -62,7 +62,7 @@ "\n", "@GreatAI.create\n", "@use_model(\"small-domain-prediction\", version=\"latest\")\n", - "@parameter(\"target_confidence\", validator=lambda c: 0 <= c <= 100)\n", + "@parameter(\"target_confidence\", validate=lambda c: 0 <= c <= 100)\n", "def predict_domain(\n", " text: str, model: Pipeline, target_confidence: int = 50\n", ") -> MultiLabelClassificationOutput:\n", diff --git a/docs/how-to-guides/call-remote.md b/docs/how-to-guides/call-remote.md index 3981fde..6b74508 100644 --- a/docs/how-to-guides/call-remote.md +++ b/docs/how-to-guides/call-remote.md @@ -43,6 +43,7 @@ results = [ print(results) ``` + 1. Only return the outputs so we don't clutter up the terminal. > Run this script as a regular Python script by executing `python3 client.py`. diff --git a/docs/how-to-guides/create-service.md b/docs/how-to-guides/create-service.md index 19307b0..7e6d7d3 100644 --- a/docs/how-to-guides/create-service.md +++ b/docs/how-to-guides/create-service.md @@ -76,7 +76,7 @@ assert type_safe_greeter('Andras').output == 'Hi Andras' ### Using `@parameter` -If you wish to turn of logging or specify custom validation for your parameters, you can use the [@parameter][great_ai.parameter] decorator. +If you wish to turn off logging or specify custom validation for your parameters, you can use the [@parameter][great_ai.parameter] decorator. !!! note By default, all parameters that are not affected by an explicit [@parameter][great_ai.parameter] or [@use_model][great_ai.use_model] are automatically decorated with [@parameter][great_ai.parameter] when [@GreatAI.create][great_ai.GreatAI.create] is called. @@ -85,7 +85,7 @@ If you wish to turn of logging or specify custom validation for your parameters, from great_ai import GreatAI, use_model @GreatAI.create -@use_model('name_of_my_model', version='latest') +@parameter('your_name', disable_logging=True) def type_safe_greeter(your_name: str, model) -> str: return f'Hi {your_name}!' @@ -105,7 +105,7 @@ from great_ai import save_model, GreatAI, parameter, use_model, log_metric save_model(4, 'secret-number') #(1) @GreatAI.create -@parameter('positive_number', validator=lambda n: n > 0, disable_logging=True) +@parameter('positive_number', validate=lambda n: n > 0, disable_logging=True) @use_model('secret-number', version='latest', model_kwarg_name='secret') def add_number(positive_number: int, secret: int) -> int: log_metric( diff --git a/docs/how-to-guides/use-service.md b/docs/how-to-guides/use-service.md index 0618478..700af6e 100644 --- a/docs/how-to-guides/use-service.md +++ b/docs/how-to-guides/use-service.md @@ -1,4 +1,4 @@ -# How to deploy a GreatAI service +# How to perform prediction with GreatAI After [creating a GreatAI service](/how-to-guides/create-service) by wrapping your prediction function, and optionally [configuring it](/how-to-guides/configure-service), it's time to do some prediction. diff --git a/docs/index.md b/docs/index.md index 195f539..a8e6bc2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,12 +25,12 @@ Applying AI is becoming increasingly easier but many case studies have shown tha ## Features - [x] Save prediction traces of each prediction including arguments and model versions -- [x] Save feedback and merge it into a ground-truth database *:arrow_right: quasi-shadow deployment* +- [x] Save feedback and merge it into a ground-truth database - [x] Version and store models and data on shared infrastructure *(MongoDB GridFS, S3-compatible storage, shared local-volume)* - [x] Automatically scaffolded custom REST API (and OpenAPI schema) for easy integration - [x] Input validation - [x] Sensible cache-policy -- [x] Seamless support for both synchronous and `async` inference methods +- [x] Seamless support for both synchronous and asynchronous inference methods - [x] Easy integration with remote GreatAI instances - [x] Built-in parallelisation (with support for multiprocessing, async, and mixed modes) for batch processing - [x] Well-tested utilities for common NLP tasks (cleaning, language-tagging, sentence-segmentation, etc.) @@ -70,7 +70,7 @@ def greeter(name: str) -> str: #(2) 2. [Typing functions](https://docs.python.org/3/library/typing.html){ target=_blank } is recommended in general, however, not required for GreatAI to work. ??? note - In practice, `greeter` could be an inference function of some AI/ML application. But it could also just wrap a black-box solution of some SaaS. Either ways, it is [imperative to have continuos oversight](https://digital-strategy.ec.europa.eu/en/library/ethics-guidelines-trustworthy-ai){ target=_blank } of the services you provide and data you process especially in the context of AI/ML applications. + In practice, `greeter` could be an inference function of some AI/ML application. But it could also just wrap a black-box solution of some SaaS. Either way, it is [imperative to have continuous oversight](https://digital-strategy.ec.europa.eu/en/library/ethics-guidelines-trustworthy-ai){ target=_blank } of the services you provide and data you process especially in the context of AI/ML applications. ```sh title="terminal" great-ai demo.py @@ -96,7 +96,7 @@ GreatAI fits between the prototype and deployment phases of your (or your organi ## Why GreatAI? -There are other, existing solutions aiming to facilitate this phase. [Amazon SageMaker](https://aws.amazon.com/sagemaker){ target=_blank } and [Seldon Core](https://www.seldon.io/solutions/open-source-projects/core){ target=_blank } provide the most comprehensive suite of features. If you have the opportunity use those, do that because they're great. +There are other, existing solutions aiming to facilitate this phase. [Amazon SageMaker](https://aws.amazon.com/sagemaker){ target=_blank } and [Seldon Core](https://www.seldon.io/solutions/open-source-projects/core){ target=_blank } provide the most comprehensive suite of features. If you have the opportunity to use them, do that because they're great. However, research indicates that professionals rarely use them. This may be due to their inherent setup and operating complexity. ==GreatAI is designed to be as simple to use as possible.== Its clear, high-level API and sensible default configuration makes it extremely easy to start using. Despite its relative simplicity over Seldon Core, it still implements many of the [SE4ML best-practices](https://se-ml.github.io){ target=_blank }, and thus, can meaningfully improve your deployment without requiring prohibitively large effort. diff --git a/docs/tutorial/deploy.ipynb b/docs/tutorial/deploy.ipynb index cfbb35e..77fcf6b 100644 --- a/docs/tutorial/deploy.ipynb +++ b/docs/tutorial/deploy.ipynb @@ -139,7 +139,7 @@ "source": [ "Congrats, you've just created your first GreatAI service! 🎉\n", "\n", - "Now that you've made sure your application is hardened enough for the intended use case, it is time to deploy it. The responsibilities of GreatAI end when it wraps your inference function and model into a production-ready service. You're given the freedom and responsibility to deploy this service. Fortunately, you (or your organisation) probably already has an established routine for deploying services.\n", + "Now that you've made sure your application is hardened enough for the intended use case, it is time to deploy it. The responsibilities of GreatAI end when it wraps your inference function and model into a production-ready service. You're given the freedom and responsibility to deploy this service. Fortunately, you (or your organisation) probably already have an established routine for deploying services.\n", "\n", "There are three main approaches to deploy a GreatAI service: For more info about them, check out [the deployment how-to](/how-to-guides/use-service).\n", "\n", diff --git a/docs/tutorial/train.ipynb b/docs/tutorial/train.ipynb index 7c6c940..fd4e12c 100644 --- a/docs/tutorial/train.ipynb +++ b/docs/tutorial/train.ipynb @@ -60,7 +60,7 @@ "source": [ "Now, we can load the dataset and extract the *training* samples from it. Since we're impatient, we can do it in parallel using the [`simple_parallel_map`](/reference/utilities/#great_ai.utilities.simple_parallel_map) function.\n", "\n", - "> Open files in Python are iterable, with a line being return (in text mode) with each iteration." + "> Open files in Python are iterable: in text mode, each iteration returns the next line." ] }, { diff --git a/great_ai/parameters/parameter.py b/great_ai/parameters/parameter.py index 1410be2..43696d3 100644 --- a/great_ai/parameters/parameter.py +++ b/great_ai/parameters/parameter.py @@ -14,7 +14,7 @@ F = TypeVar("F", bound=Callable) def parameter( parameter_name: str, *, - validator: Callable[[Any], bool] = lambda _: True, + validate: Callable[[Any], bool] = lambda _: True, disable_logging: bool = False, ) -> Callable[[F], F]: """Control the validation and logging of function parameters. @@ -30,7 +30,7 @@ def parameter( ... TypeError: type of a must be int; got str instead - >>> @parameter('positive_a', validator=lambda v: v > 0) + >>> @parameter('positive_a', validate=lambda v: v > 0) ... def my_function(positive_a: int): ... return a + 2 >>> my_function(-1) @@ -40,7 +40,8 @@ def parameter( Args: parameter_name: Name of parameter to consider - validator: Optional validator to run against the concrete argument. ArgumentValidationError is thrown when the return value is False. + validate: Optional validate to run against the concrete argument. + ArgumentValidationError is thrown when the return value is False. disable_logging: Do not save the value in any active TracingContext. Returns: A decorator for argument validation. @@ -62,9 +63,11 @@ def parameter( if expected_type is not None: check_type(parameter_name, argument, expected_type) - if not validator(argument): + if not validate(argument): raise ArgumentValidationError( - f"Argument {parameter_name} in {func.__name__} did not pass validation" + f"""Argument {parameter_name} in { + func.__name__ + } did not pass validation""" ) context = TracingContext.get_current_tracing_context() diff --git a/tests/test_async_starters.py b/tests/test_async_starters.py index 16eb06d..d25a3d0 100644 --- a/tests/test_async_starters.py +++ b/tests/test_async_starters.py @@ -24,7 +24,7 @@ async def test_create_trivial_cases() -> None: @pytest.mark.asyncio async def test_with_parameter() -> None: @GreatAI.create - @parameter("name", validator=lambda v: len(v) > 5) + @parameter("name", validate=lambda v: len(v) > 5) async def hello_world(name: str) -> str: await sleep(0.5) return f"Hello {name}!" @@ -33,7 +33,7 @@ async def test_with_parameter() -> None: @pytest.mark.asyncio async def test_with_parameters() -> None: @GreatAI.create - @parameter("name", validator=lambda v: len(v) > 5) + @parameter("name", validate=lambda v: len(v) > 5) @parameter("unused", disable_logging=True) async def hello_world(name: str, unused) -> str: # type: ignore await sleep(0.5) @@ -45,7 +45,7 @@ async def test_with_parameters() -> None: def test_wrong_order() -> None: with pytest.raises(WrongDecoratorOrderError): - @parameter("name", validator=lambda v: len(v) > 5) + @parameter("name", validate=lambda v: len(v) > 5) @GreatAI.create async def hello_world(name: str) -> str: return f"Hello {name}!" diff --git a/tests/test_basic_starters.py b/tests/test_basic_starters.py index fc13da7..d7a8052 100644 --- a/tests/test_basic_starters.py +++ b/tests/test_basic_starters.py @@ -41,7 +41,7 @@ def test_create_with_other_decorator() -> None: def test_with_parameter() -> None: @GreatAI.create - @parameter("name", validator=lambda v: len(v) > 5) + @parameter("name", validate=lambda v: len(v) > 5) def hello_world(name: str) -> str: return f"Hello {name}!" @@ -54,7 +54,7 @@ def test_with_parameter() -> None: def test_wrong_order() -> None: with pytest.raises(WrongDecoratorOrderError): - @parameter("name", validator=lambda v: len(v) > 5) + @parameter("name", validate=lambda v: len(v) > 5) @GreatAI.create def hello_world(name: str) -> str: return f"Hello {name}!"