fastmcp 0.4.1__py3-none-any.whl → 2.0.0__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.
- fastmcp/__init__.py +15 -4
- fastmcp/cli/__init__.py +0 -1
- fastmcp/cli/claude.py +13 -11
- fastmcp/cli/cli.py +61 -41
- fastmcp/client/__init__.py +25 -0
- fastmcp/client/base.py +1 -0
- fastmcp/client/client.py +181 -0
- fastmcp/client/roots.py +75 -0
- fastmcp/client/sampling.py +50 -0
- fastmcp/client/transports.py +411 -0
- fastmcp/prompts/__init__.py +1 -1
- fastmcp/prompts/base.py +27 -26
- fastmcp/prompts/prompt_manager.py +50 -12
- fastmcp/resources/__init__.py +5 -5
- fastmcp/resources/base.py +2 -2
- fastmcp/resources/resource_manager.py +66 -9
- fastmcp/resources/templates.py +15 -10
- fastmcp/resources/types.py +16 -11
- fastmcp/server/__init__.py +5 -0
- fastmcp/server/context.py +222 -0
- fastmcp/server/openapi.py +625 -0
- fastmcp/server/proxy.py +219 -0
- fastmcp/{server.py → server/server.py} +251 -265
- fastmcp/settings.py +73 -0
- fastmcp/tools/base.py +28 -18
- fastmcp/tools/tool_manager.py +45 -10
- fastmcp/utilities/func_metadata.py +33 -19
- fastmcp/utilities/openapi.py +797 -0
- fastmcp/utilities/types.py +3 -4
- fastmcp-2.0.0.dist-info/METADATA +770 -0
- fastmcp-2.0.0.dist-info/RECORD +39 -0
- {fastmcp-0.4.1.dist-info → fastmcp-2.0.0.dist-info}/WHEEL +1 -1
- fastmcp-2.0.0.dist-info/licenses/LICENSE +201 -0
- fastmcp/prompts/manager.py +0 -50
- fastmcp-0.4.1.dist-info/METADATA +0 -587
- fastmcp-0.4.1.dist-info/RECORD +0 -28
- fastmcp-0.4.1.dist-info/licenses/LICENSE +0 -21
- {fastmcp-0.4.1.dist-info → fastmcp-2.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Resource manager functionality."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from pydantic import AnyUrl
|
|
6
7
|
|
|
@@ -15,8 +16,8 @@ class ResourceManager:
|
|
|
15
16
|
"""Manages FastMCP resources."""
|
|
16
17
|
|
|
17
18
|
def __init__(self, warn_on_duplicate_resources: bool = True):
|
|
18
|
-
self._resources:
|
|
19
|
-
self._templates:
|
|
19
|
+
self._resources: dict[str, Resource] = {}
|
|
20
|
+
self._templates: dict[str, ResourceTemplate] = {}
|
|
20
21
|
self.warn_on_duplicate_resources = warn_on_duplicate_resources
|
|
21
22
|
|
|
22
23
|
def add_resource(self, resource: Resource) -> Resource:
|
|
@@ -34,7 +35,7 @@ class ResourceManager:
|
|
|
34
35
|
extra={
|
|
35
36
|
"uri": resource.uri,
|
|
36
37
|
"type": type(resource).__name__,
|
|
37
|
-
"
|
|
38
|
+
"resource_name": resource.name,
|
|
38
39
|
},
|
|
39
40
|
)
|
|
40
41
|
existing = self._resources.get(str(resource.uri))
|
|
@@ -47,11 +48,11 @@ class ResourceManager:
|
|
|
47
48
|
|
|
48
49
|
def add_template(
|
|
49
50
|
self,
|
|
50
|
-
fn: Callable,
|
|
51
|
+
fn: Callable[..., Any],
|
|
51
52
|
uri_template: str,
|
|
52
|
-
name:
|
|
53
|
-
description:
|
|
54
|
-
mime_type:
|
|
53
|
+
name: str | None = None,
|
|
54
|
+
description: str | None = None,
|
|
55
|
+
mime_type: str | None = None,
|
|
55
56
|
) -> ResourceTemplate:
|
|
56
57
|
"""Add a template from a function."""
|
|
57
58
|
template = ResourceTemplate.from_function(
|
|
@@ -64,7 +65,7 @@ class ResourceManager:
|
|
|
64
65
|
self._templates[template.uri_template] = template
|
|
65
66
|
return template
|
|
66
67
|
|
|
67
|
-
async def get_resource(self, uri:
|
|
68
|
+
async def get_resource(self, uri: AnyUrl | str) -> Resource | None:
|
|
68
69
|
"""Get resource by URI, checking concrete resources first, then templates."""
|
|
69
70
|
uri_str = str(uri)
|
|
70
71
|
logger.debug("Getting resource", extra={"uri": uri_str})
|
|
@@ -92,3 +93,59 @@ class ResourceManager:
|
|
|
92
93
|
"""List all registered templates."""
|
|
93
94
|
logger.debug("Listing templates", extra={"count": len(self._templates)})
|
|
94
95
|
return list(self._templates.values())
|
|
96
|
+
|
|
97
|
+
def import_resources(
|
|
98
|
+
self, manager: "ResourceManager", prefix: str | None = None
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Import resources from another resource manager.
|
|
101
|
+
|
|
102
|
+
Resources are imported with a prefixed URI if a prefix is provided. For example,
|
|
103
|
+
if a resource has URI "data://users" and you import it with prefix "app+", the
|
|
104
|
+
imported resource will have URI "app+data://users". If no prefix is provided,
|
|
105
|
+
the original URI is used.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
manager: The ResourceManager to import from
|
|
109
|
+
prefix: A prefix to apply to the resource URIs, including the delimiter.
|
|
110
|
+
For example, "app+" would result in URIs like "app+data://users".
|
|
111
|
+
If None, the original URI is used.
|
|
112
|
+
"""
|
|
113
|
+
for uri, resource in manager._resources.items():
|
|
114
|
+
# Create prefixed URI and copy the resource with the new URI
|
|
115
|
+
prefixed_uri = f"{prefix}{uri}" if prefix else uri
|
|
116
|
+
|
|
117
|
+
# Log the import
|
|
118
|
+
logger.debug(f"Importing resource with URI {uri} as {prefixed_uri}")
|
|
119
|
+
|
|
120
|
+
# Store directly in resources dictionary
|
|
121
|
+
self._resources[prefixed_uri] = resource
|
|
122
|
+
|
|
123
|
+
def import_templates(
|
|
124
|
+
self, manager: "ResourceManager", prefix: str | None = None
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Import resource templates from another resource manager.
|
|
127
|
+
|
|
128
|
+
Templates are imported with a prefixed URI template if a prefix is provided.
|
|
129
|
+
For example, if a template has URI template "data://users/{id}" and you import
|
|
130
|
+
it with prefix "app+", the imported template will have URI template
|
|
131
|
+
"app+data://users/{id}". If no prefix is provided, the original URI template is used.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
manager: The ResourceManager to import templates from
|
|
135
|
+
prefix: A prefix to apply to the template URIs, including the delimiter.
|
|
136
|
+
For example, "app+" would result in URI templates like "app+data://users/{id}".
|
|
137
|
+
If None, the original URI template is used.
|
|
138
|
+
"""
|
|
139
|
+
for uri_template, template in manager._templates.items():
|
|
140
|
+
# Create prefixed URI template and copy the template with the new URI template
|
|
141
|
+
prefixed_uri_template = (
|
|
142
|
+
f"{prefix}{uri_template}" if prefix else uri_template
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Log the import
|
|
146
|
+
logger.debug(
|
|
147
|
+
f"Importing resource template with URI {uri_template} as {prefixed_uri_template}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Store directly in templates dictionary
|
|
151
|
+
self._templates[prefixed_uri_template] = template
|
fastmcp/resources/templates.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Resource template functionality."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import inspect
|
|
4
6
|
import re
|
|
5
|
-
from
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Any
|
|
6
9
|
|
|
7
10
|
from pydantic import BaseModel, Field, TypeAdapter, validate_call
|
|
8
11
|
|
|
@@ -20,18 +23,20 @@ class ResourceTemplate(BaseModel):
|
|
|
20
23
|
mime_type: str = Field(
|
|
21
24
|
default="text/plain", description="MIME type of the resource content"
|
|
22
25
|
)
|
|
23
|
-
fn: Callable = Field(exclude=True)
|
|
24
|
-
parameters: dict = Field(
|
|
26
|
+
fn: Callable[..., Any] = Field(exclude=True)
|
|
27
|
+
parameters: dict[str, Any] = Field(
|
|
28
|
+
description="JSON schema for function parameters"
|
|
29
|
+
)
|
|
25
30
|
|
|
26
31
|
@classmethod
|
|
27
32
|
def from_function(
|
|
28
33
|
cls,
|
|
29
|
-
fn: Callable,
|
|
34
|
+
fn: Callable[..., Any],
|
|
30
35
|
uri_template: str,
|
|
31
|
-
name:
|
|
32
|
-
description:
|
|
33
|
-
mime_type:
|
|
34
|
-
) ->
|
|
36
|
+
name: str | None = None,
|
|
37
|
+
description: str | None = None,
|
|
38
|
+
mime_type: str | None = None,
|
|
39
|
+
) -> ResourceTemplate:
|
|
35
40
|
"""Create a template from a function."""
|
|
36
41
|
func_name = name or fn.__name__
|
|
37
42
|
if func_name == "<lambda>":
|
|
@@ -52,7 +57,7 @@ class ResourceTemplate(BaseModel):
|
|
|
52
57
|
parameters=parameters,
|
|
53
58
|
)
|
|
54
59
|
|
|
55
|
-
def matches(self, uri: str) ->
|
|
60
|
+
def matches(self, uri: str) -> dict[str, Any] | None:
|
|
56
61
|
"""Check if URI matches template and extract parameters."""
|
|
57
62
|
# Convert template to regex pattern
|
|
58
63
|
pattern = self.uri_template.replace("{", "(?P<").replace("}", ">[^/]+)")
|
|
@@ -61,7 +66,7 @@ class ResourceTemplate(BaseModel):
|
|
|
61
66
|
return match.groupdict()
|
|
62
67
|
return None
|
|
63
68
|
|
|
64
|
-
async def create_resource(self, uri: str, params:
|
|
69
|
+
async def create_resource(self, uri: str, params: dict[str, Any]) -> Resource:
|
|
65
70
|
"""Create a resource from the template with the given parameters."""
|
|
66
71
|
try:
|
|
67
72
|
# Call function and check if result is a coroutine
|
fastmcp/resources/types.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Concrete resource implementations."""
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import inspect
|
|
4
4
|
import json
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
7
8
|
|
|
9
|
+
import anyio
|
|
10
|
+
import anyio.to_thread
|
|
8
11
|
import httpx
|
|
9
12
|
import pydantic.json
|
|
10
13
|
import pydantic_core
|
|
@@ -48,10 +51,12 @@ class FunctionResource(Resource):
|
|
|
48
51
|
|
|
49
52
|
fn: Callable[[], Any] = Field(exclude=True)
|
|
50
53
|
|
|
51
|
-
async def read(self) ->
|
|
54
|
+
async def read(self) -> str | bytes:
|
|
52
55
|
"""Read the resource by calling the wrapped function."""
|
|
53
56
|
try:
|
|
54
|
-
result =
|
|
57
|
+
result = (
|
|
58
|
+
await self.fn() if inspect.iscoroutinefunction(self.fn) else self.fn()
|
|
59
|
+
)
|
|
55
60
|
if isinstance(result, Resource):
|
|
56
61
|
return await result.read()
|
|
57
62
|
if isinstance(result, bytes):
|
|
@@ -100,12 +105,12 @@ class FileResource(Resource):
|
|
|
100
105
|
mime_type = info.data.get("mime_type", "text/plain")
|
|
101
106
|
return not mime_type.startswith("text/")
|
|
102
107
|
|
|
103
|
-
async def read(self) ->
|
|
108
|
+
async def read(self) -> str | bytes:
|
|
104
109
|
"""Read the file content."""
|
|
105
110
|
try:
|
|
106
111
|
if self.is_binary:
|
|
107
|
-
return await
|
|
108
|
-
return await
|
|
112
|
+
return await anyio.to_thread.run_sync(self.path.read_bytes)
|
|
113
|
+
return await anyio.to_thread.run_sync(self.path.read_text)
|
|
109
114
|
except Exception as e:
|
|
110
115
|
raise ValueError(f"Error reading file {self.path}: {e}")
|
|
111
116
|
|
|
@@ -114,11 +119,11 @@ class HttpResource(Resource):
|
|
|
114
119
|
"""A resource that reads from an HTTP endpoint."""
|
|
115
120
|
|
|
116
121
|
url: str = Field(description="URL to fetch content from")
|
|
117
|
-
mime_type: str
|
|
122
|
+
mime_type: str = Field(
|
|
118
123
|
default="application/json", description="MIME type of the resource content"
|
|
119
124
|
)
|
|
120
125
|
|
|
121
|
-
async def read(self) ->
|
|
126
|
+
async def read(self) -> str | bytes:
|
|
122
127
|
"""Read the HTTP content."""
|
|
123
128
|
async with httpx.AsyncClient() as client:
|
|
124
129
|
response = await client.get(self.url)
|
|
@@ -136,7 +141,7 @@ class DirectoryResource(Resource):
|
|
|
136
141
|
pattern: str | None = Field(
|
|
137
142
|
default=None, description="Optional glob pattern to filter files"
|
|
138
143
|
)
|
|
139
|
-
mime_type: str
|
|
144
|
+
mime_type: str = Field(
|
|
140
145
|
default="application/json", description="MIME type of the resource content"
|
|
141
146
|
)
|
|
142
147
|
|
|
@@ -173,7 +178,7 @@ class DirectoryResource(Resource):
|
|
|
173
178
|
async def read(self) -> str: # Always returns JSON string
|
|
174
179
|
"""Read the directory listing."""
|
|
175
180
|
try:
|
|
176
|
-
files = await
|
|
181
|
+
files = await anyio.to_thread.run_sync(self.list_files)
|
|
177
182
|
file_list = [str(f.relative_to(self.path)) for f in files if f.is_file()]
|
|
178
183
|
return json.dumps({"files": file_list}, indent=2)
|
|
179
184
|
except Exception as e:
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from __future__ import annotations as _annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Generic, Literal
|
|
4
|
+
|
|
5
|
+
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
|
6
|
+
from mcp.server.session import ServerSessionT
|
|
7
|
+
from mcp.shared.context import LifespanContextT, RequestContext
|
|
8
|
+
from mcp.types import (
|
|
9
|
+
CreateMessageResult,
|
|
10
|
+
ImageContent,
|
|
11
|
+
Root,
|
|
12
|
+
SamplingMessage,
|
|
13
|
+
TextContent,
|
|
14
|
+
)
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
from pydantic.networks import AnyUrl
|
|
17
|
+
|
|
18
|
+
from fastmcp.server.server import FastMCP
|
|
19
|
+
from fastmcp.utilities.logging import get_logger
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Context(BaseModel, Generic[ServerSessionT, LifespanContextT]):
|
|
25
|
+
"""Context object providing access to MCP capabilities.
|
|
26
|
+
|
|
27
|
+
This provides a cleaner interface to MCP's RequestContext functionality.
|
|
28
|
+
It gets injected into tool and resource functions that request it via type hints.
|
|
29
|
+
|
|
30
|
+
To use context in a tool function, add a parameter with the Context type annotation:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
@server.tool()
|
|
34
|
+
def my_tool(x: int, ctx: Context) -> str:
|
|
35
|
+
# Log messages to the client
|
|
36
|
+
ctx.info(f"Processing {x}")
|
|
37
|
+
ctx.debug("Debug info")
|
|
38
|
+
ctx.warning("Warning message")
|
|
39
|
+
ctx.error("Error message")
|
|
40
|
+
|
|
41
|
+
# Report progress
|
|
42
|
+
ctx.report_progress(50, 100)
|
|
43
|
+
|
|
44
|
+
# Access resources
|
|
45
|
+
data = ctx.read_resource("resource://data")
|
|
46
|
+
|
|
47
|
+
# Get request info
|
|
48
|
+
request_id = ctx.request_id
|
|
49
|
+
client_id = ctx.client_id
|
|
50
|
+
|
|
51
|
+
return str(x)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The context parameter name can be anything as long as it's annotated with Context.
|
|
55
|
+
The context is optional - tools that don't need it can omit the parameter.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
_request_context: RequestContext[ServerSessionT, LifespanContextT] | None
|
|
59
|
+
_fastmcp: FastMCP | None
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
*,
|
|
64
|
+
request_context: RequestContext[ServerSessionT, LifespanContextT] | None = None,
|
|
65
|
+
fastmcp: FastMCP | None = None,
|
|
66
|
+
**kwargs: Any,
|
|
67
|
+
):
|
|
68
|
+
super().__init__(**kwargs)
|
|
69
|
+
self._request_context = request_context
|
|
70
|
+
self._fastmcp = fastmcp
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def fastmcp(self) -> FastMCP:
|
|
74
|
+
"""Access to the FastMCP server."""
|
|
75
|
+
if self._fastmcp is None:
|
|
76
|
+
raise ValueError("Context is not available outside of a request")
|
|
77
|
+
return self._fastmcp
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def request_context(self) -> RequestContext[ServerSessionT, LifespanContextT]:
|
|
81
|
+
"""Access to the underlying request context."""
|
|
82
|
+
if self._request_context is None:
|
|
83
|
+
raise ValueError("Context is not available outside of a request")
|
|
84
|
+
return self._request_context
|
|
85
|
+
|
|
86
|
+
async def report_progress(
|
|
87
|
+
self, progress: float, total: float | None = None
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Report progress for the current operation.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
progress: Current progress value e.g. 24
|
|
93
|
+
total: Optional total value e.g. 100
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
progress_token = (
|
|
97
|
+
self.request_context.meta.progressToken
|
|
98
|
+
if self.request_context.meta
|
|
99
|
+
else None
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if progress_token is None:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
await self.request_context.session.send_progress_notification(
|
|
106
|
+
progress_token=progress_token, progress=progress, total=total
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
async def read_resource(self, uri: str | AnyUrl) -> list[ReadResourceContents]:
|
|
110
|
+
"""Read a resource by URI.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
uri: Resource URI to read
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
The resource content as either text or bytes
|
|
117
|
+
"""
|
|
118
|
+
assert self._fastmcp is not None, (
|
|
119
|
+
"Context is not available outside of a request"
|
|
120
|
+
)
|
|
121
|
+
return await self._fastmcp.read_resource(uri)
|
|
122
|
+
|
|
123
|
+
async def log(
|
|
124
|
+
self,
|
|
125
|
+
level: Literal["debug", "info", "warning", "error"],
|
|
126
|
+
message: str,
|
|
127
|
+
*,
|
|
128
|
+
logger_name: str | None = None,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Send a log message to the client.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
level: Log level (debug, info, warning, error)
|
|
134
|
+
message: Log message
|
|
135
|
+
logger_name: Optional logger name
|
|
136
|
+
**extra: Additional structured data to include
|
|
137
|
+
"""
|
|
138
|
+
await self.request_context.session.send_log_message(
|
|
139
|
+
level=level, data=message, logger=logger_name
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def client_id(self) -> str | None:
|
|
144
|
+
"""Get the client ID if available."""
|
|
145
|
+
return (
|
|
146
|
+
getattr(self.request_context.meta, "client_id", None)
|
|
147
|
+
if self.request_context.meta
|
|
148
|
+
else None
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def request_id(self) -> str:
|
|
153
|
+
"""Get the unique ID for this request."""
|
|
154
|
+
return str(self.request_context.request_id)
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def session(self):
|
|
158
|
+
"""Access to the underlying session for advanced usage."""
|
|
159
|
+
return self.request_context.session
|
|
160
|
+
|
|
161
|
+
# Convenience methods for common log levels
|
|
162
|
+
async def debug(self, message: str, **extra: Any) -> None:
|
|
163
|
+
"""Send a debug log message."""
|
|
164
|
+
await self.log("debug", message, **extra)
|
|
165
|
+
|
|
166
|
+
async def info(self, message: str, **extra: Any) -> None:
|
|
167
|
+
"""Send an info log message."""
|
|
168
|
+
await self.log("info", message, **extra)
|
|
169
|
+
|
|
170
|
+
async def warning(self, message: str, **extra: Any) -> None:
|
|
171
|
+
"""Send a warning log message."""
|
|
172
|
+
await self.log("warning", message, **extra)
|
|
173
|
+
|
|
174
|
+
async def error(self, message: str, **extra: Any) -> None:
|
|
175
|
+
"""Send an error log message."""
|
|
176
|
+
await self.log("error", message, **extra)
|
|
177
|
+
|
|
178
|
+
async def list_roots(self) -> list[Root]:
|
|
179
|
+
"""List the roots available to the server, as indicated by the client."""
|
|
180
|
+
result = await self.request_context.session.list_roots()
|
|
181
|
+
return result.roots
|
|
182
|
+
|
|
183
|
+
async def sample(
|
|
184
|
+
self,
|
|
185
|
+
messages: str | list[str | SamplingMessage],
|
|
186
|
+
system_prompt: str | None = None,
|
|
187
|
+
temperature: float | None = None,
|
|
188
|
+
max_tokens: int | None = None,
|
|
189
|
+
) -> TextContent | ImageContent:
|
|
190
|
+
"""
|
|
191
|
+
Send a sampling request to the client and await the response.
|
|
192
|
+
|
|
193
|
+
Call this method at any time to have the server request an LLM
|
|
194
|
+
completion from the client. The client must be appropriately configured,
|
|
195
|
+
or the request will error.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
if max_tokens is None:
|
|
199
|
+
max_tokens = 512
|
|
200
|
+
|
|
201
|
+
if isinstance(messages, str):
|
|
202
|
+
sampling_messages = [
|
|
203
|
+
SamplingMessage(
|
|
204
|
+
content=TextContent(text=messages, type="text"), role="user"
|
|
205
|
+
)
|
|
206
|
+
]
|
|
207
|
+
elif isinstance(messages, list):
|
|
208
|
+
sampling_messages = [
|
|
209
|
+
SamplingMessage(content=TextContent(text=m, type="text"), role="user")
|
|
210
|
+
if isinstance(m, str)
|
|
211
|
+
else m
|
|
212
|
+
for m in messages
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
result: CreateMessageResult = await self.request_context.session.create_message(
|
|
216
|
+
messages=sampling_messages,
|
|
217
|
+
system_prompt=system_prompt,
|
|
218
|
+
temperature=temperature,
|
|
219
|
+
max_tokens=max_tokens,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return result.content
|