ivcap-lambda 0.7.22__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ivcap_lambda/utils.py ADDED
@@ -0,0 +1,100 @@
1
+ #
2
+ # Copyright (c) 2023 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
3
+ # Use of this source code is governed by a BSD-style license that can be
4
+ # found in the LICENSE file. See the AUTHORS file for names of contributors.
5
+ #
6
+ from typing import Optional, Dict, Tuple
7
+
8
+ def get_title_from_path(path: str) -> Tuple[str, str]:
9
+ """Extracts a title from a path string.
10
+
11
+ Args:
12
+ path: A string potentially formatted as a directory path (separated by '/').
13
+
14
+ Returns:
15
+ A tuple containing:
16
+ - The processed string with the first character in lowercase.
17
+ - The processed string with the first character in uppercase.
18
+ """
19
+ # Extract the last element of the path
20
+ last_element = path.split('/')[-1]
21
+
22
+ # Convert to singular form if plural
23
+ singular = last_element
24
+ if singular.endswith('ies'):
25
+ singular = singular[:-3] + 'y'
26
+ elif singular.endswith('es'):
27
+ singular = singular[:-2]
28
+ elif singular.endswith('ss'):
29
+ pass
30
+ elif singular.endswith('s'):
31
+ singular = singular[:-1]
32
+
33
+ # Replace underscores with spaces
34
+ singular = singular.replace('_', ' ')
35
+
36
+ # Create lowercase and uppercase versions
37
+ if singular:
38
+ lowercase = singular[0].lower() + singular[1:] if len(singular) > 1 else singular.lower()
39
+ uppercase = singular[0].upper() + singular[1:] if len(singular) > 1 else singular.upper()
40
+ else:
41
+ lowercase = ""
42
+ uppercase = ""
43
+
44
+ return (lowercase, uppercase)
45
+
46
+ def find_first(iterable, condition):
47
+ """
48
+ Returns the first item in the iterable for which the condition is True.
49
+ If no such item is found, returns None.
50
+ """
51
+ for item in iterable:
52
+ if condition(item):
53
+ return item
54
+ return None
55
+
56
+ from fastapi import Request
57
+ from typing import Optional, Dict, Tuple
58
+
59
+
60
+ def get_public_url_prefix(req: Request) -> str:
61
+ """Return the public url prefix for `req`.
62
+
63
+ First checks for a `Forwarded` http header and if
64
+ absent uses the request's `base_ur;` variable.
65
+
66
+ Args:
67
+ req (Request): A FastAPI request instance
68
+
69
+ Returns:
70
+ str: A url as string
71
+ """
72
+ fw = get_forwarded_header(req)
73
+ if fw != None:
74
+ prefix = f"{fw.get('proto', 'http')}:://{fw.get('for')}"
75
+ else:
76
+ prefix = str(req.base_url).rstrip("/")
77
+ return prefix
78
+
79
+
80
+ def get_forwarded_header(request: Request) -> Optional[Dict[str, str]]:
81
+ """
82
+ Parses the "Forwarded" HTTP header according to RFC 7239.
83
+ Returns a dictionary containing the parsed header values, or None if the header is missing.
84
+ """
85
+ header_value = request.headers.get("Forwarded")
86
+ if not header_value:
87
+ return None
88
+
89
+ parsed_values: Dict[str, str] = {}
90
+ for element in header_value.split(";"):
91
+ parts = element.split("=", 1)
92
+ if len(parts) == 2:
93
+ key = parts[0].strip()
94
+ value = parts[1].strip()
95
+ # Remove quotes if present
96
+ if value.startswith('"') and value.endswith('"'):
97
+ value = value[1:-1]
98
+ parsed_values[key] = value
99
+
100
+ return parsed_values
@@ -0,0 +1,22 @@
1
+ #
2
+ # Copyright (c) 2023 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
3
+ # Use of this source code is governed by a BSD-style license that can be
4
+ # found in the LICENSE file. See the AUTHORS file for names of contributors.
5
+ #
6
+
7
+ __version__ = "???"
8
+ try: # Python > 3.8+
9
+ from importlib_metadata import version
10
+
11
+ __version__ = version("ivcap_lambda")
12
+ except ImportError:
13
+ try:
14
+ import pkg_resources
15
+
16
+ __version__ = pkg_resources.get_distribution("ivcap_lambda").version
17
+ except Exception:
18
+ pass
19
+
20
+
21
+ def get_version():
22
+ return __version__
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: ivcap-lambda
3
+ Version: 0.7.22
4
+ Summary: Helper functions for building lambda-style services on the IVCAP platform
5
+ License-File: AUTHORS.md
6
+ License-File: LICENSE
7
+ Author: Max Ott
8
+ Author-email: max.ott@csiro.au
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: cachetools (>=5.5.2,<6.0.0)
16
+ Requires-Dist: fastapi (>=0.121.2,<0.122.0)
17
+ Requires-Dist: ivcap-service (>=0.6.21,<0.7.0)
18
+ Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.57b0)
19
+ Requires-Dist: uuid6 (==2024.7.10)
20
+ Requires-Dist: uvicorn (>=0.38.0,<0.39.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # ivcap-lambda: A python library for building lambda-style services on the IVCAP platform
24
+
25
+ > **Package renamed:** `ivcap-ai-tool` has been renamed to `ivcap-lambda` to reflect that
26
+ > the library is useful for any lambda-style IVCAP service, not just AI agent tools.
27
+ > A [compatibility shim](./compat/) is published under the old name — existing apps
28
+ > will continue to work but will see a `DeprecationWarning` prompting migration.
29
+
30
+ <a href="https://scan.coverity.com/projects/ivcap-works-ivcap-ai-tool-sdk-python">
31
+ <img alt="Coverity Scan Build Status"
32
+ src="https://img.shields.io/coverity/scan/31491.svg"/>
33
+ </a>
34
+
35
+ A python library containing various helper and middleware functions
36
+ to simplify developing AI tools to be deployed on IVCAP.
37
+
38
+ > **Note:** A template git repositiory using this library can be found on github
39
+ [ivcap-works/ivcap-python-ai-tool-template](https://github.com/ivcap-works/ivcap-python-ai-tool-template). You may clone that and start from there.
40
+
41
+ ## Content
42
+
43
+ * [Register a Tool Function](#register)
44
+ * [Start the Service](#start)
45
+ * [JSON-RPC Middleware](#json-rpc)
46
+ * [Try-Later Middleware](#try-later)
47
+
48
+ ### Register a Tool Function <a name="register"></a>
49
+
50
+ ```python
51
+ class Request(BaseModel):
52
+ jschema: str = Field("urn:sd:schema:some_tool.request.1", alias="$schema")
53
+ ...
54
+
55
+ class Result(BaseModel):
56
+ jschema: str = Field("urn:sd:schema:some_tool.1", alias="$schema")
57
+ ...
58
+
59
+ def some_tool(req: Request) -> Result:
60
+ """
61
+ Here should go a quite extensive description of what the tool can be
62
+ used for so that an agent can work out if this tool is useful in
63
+ a specific context.
64
+
65
+ DO NOT ADD PARAMTER AND RETURN DECRIPTIONS -
66
+ DESCRIBE THEM IN THE `Request` MODEL
67
+ """
68
+ ...
69
+
70
+ return Result(...)
71
+
72
+ add_tool_api_route(app, "/", some_tool, opts=ToolOptions(tags=["Great Tool"]))
73
+ ```
74
+
75
+ ### Start the Service <a name="start"></a>
76
+
77
+ ```python
78
+ app = FastAPI(
79
+ ..
80
+ )
81
+
82
+ if __name__ == "__main__":
83
+ start_tool_server(app, some_tool)
84
+ ```
85
+
86
+ ### JSON-RPC Middleware <a name="json-rpc"></a>
87
+
88
+ This middleware will convert any `POST /` with a payload
89
+ following the [JSON-RPC](https://www.jsonrpc.org/specification)
90
+ specification to an internal `POST /{method}` and will return
91
+ the result formatted according to the JSON-RPC spec.
92
+
93
+ ```python
94
+ from ivcap_fastapi import use_json_rpc_middleware
95
+
96
+ app = FastAPI(
97
+ ..
98
+ )
99
+
100
+ use_json_rpc_middleware(app)
101
+ ```
102
+
103
+ ### Try-Later Middleware <a name="try-later"></a>
104
+
105
+ This middleware is supporting the use case where the execution of a
106
+ requested service is taking longer than the caller is willing to wait.
107
+ A typical use case is where the service is itself outsourcing the execution
108
+ to some other long-running service but may immediately receive a reference
109
+ to the eventual result.
110
+
111
+ In this case, raising a `TryLaterException` will return with a 204
112
+ status code and additional information on how to later check back for the
113
+ result.
114
+
115
+ ```python
116
+ from ivcap_fastapi import TryLaterException, use_try_later_middleware
117
+ use_try_later_middleware(app)
118
+
119
+ @app.post("/big_job")
120
+ def big_job(req: Request) -> Response:
121
+ jobID, expected_exec_time = scheduling_big_job(req)
122
+ raise TryLaterException(f"/big_job/jobs/{jobID}", expected_exec_time)
123
+
124
+ @app.get("/big_job/jobs/{jobID}")
125
+ def get_job(jobID: str) -> Response:
126
+ resp = find_result_for(job_id)
127
+ return resp
128
+ ```
129
+
130
+ Specifically, raising `TryLaterException(location, delay)` will
131
+ return an HTTP response with a 204 status code with the additional
132
+ HTTP headers `Location` and `Retry-Later` set to `location` and
133
+ `delay` respectively.
134
+
@@ -0,0 +1,18 @@
1
+ ivcap_lambda/__init__.py,sha256=vQouZe1fq35W5UC2621EPOOni_xalbp9Zm6yxSDiPMI,777
2
+ ivcap_lambda/builder.py,sha256=MEcVs4Ey-WC6yRMIIzi-Phww8je37ToacVtKjt4VvHk,10779
3
+ ivcap_lambda/decorators.py,sha256=DgLOqKAYVh1QvJNDcsFqoIzXloagIflBeBO-WeNdy0U,2813
4
+ ivcap_lambda/executor.py,sha256=RwpZq9-Sm2RBUXqnskQUr580KF4zt9r0Vcm60_mF3uY,11154
5
+ ivcap_lambda/logger.py,sha256=yVioKPXVUIjJ5WzSA0_cZxr4jthM8K7edO3px29rnLA,1327
6
+ ivcap_lambda/logging.json,sha256=EBreTGZe9l1OU_MylU4uigwA8Zs_RBPCr_UDWHCVAps,1642
7
+ ivcap_lambda/mcp.py,sha256=WJgLLD3sWxd9vxhHnqPTf0zMENgNgaCCJEjDKZ162hU,8250
8
+ ivcap_lambda/py.typed,sha256=8ZJUsxZiuOy1oJeVhsTWQhTG_6pTVHVXk5hJL79ebTk,25
9
+ ivcap_lambda/secret.py,sha256=iLXhuA0rfXLIlahVp7htk9_MF5mpfccLa6gqCp-OAEs,1300
10
+ ivcap_lambda/server.py,sha256=17TIO3k9ThTNwoXYbEXkWvTIkPvEdS5esExy4U4Qyd0,6629
11
+ ivcap_lambda/service_definition.py,sha256=ueuvo4Q5TxmBao-zeNP8qfYuVJwdXPAuYaOPGd12DdI,1645
12
+ ivcap_lambda/utils.py,sha256=PhUwlMDO8_IHIPonJo8ZKX9mJimrmRDluhfkDzGGCwY,3145
13
+ ivcap_lambda/version.py,sha256=doCn_9Qf-MMhIb5a7LaVxn3YDfU0SVH2QebEUTYe8zg,610
14
+ ivcap_lambda-0.7.22.dist-info/METADATA,sha256=3i1id6WPuwOaGuCEDz0-MJTIXit9frM2kfbr31UuVy8,4397
15
+ ivcap_lambda-0.7.22.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
16
+ ivcap_lambda-0.7.22.dist-info/licenses/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
17
+ ivcap_lambda-0.7.22.dist-info/licenses/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
18
+ ivcap_lambda-0.7.22.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.4.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ # Initial version authors
2
+
3
+ * Max Ott <max.ott@csiro.au>
4
+
5
+ # Partial list of contributors
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.