mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Merge branch 'main' into dev/dotnet_workflow/magentic
This commit is contained in:
@@ -98,7 +98,7 @@
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.InMemory" Version="1.67.0-preview" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.67.0-preview" />
|
||||
<!-- Agent SDKs -->
|
||||
<PackageVersion Include="GitHub.Copilot.SDK" Version="0.1.29" />
|
||||
<PackageVersion Include="GitHub.Copilot.SDK" Version="1.0.0-beta.2" />
|
||||
<PackageVersion Include="Microsoft.Agents.CopilotStudio.Client" Version="1.3.171-beta" />
|
||||
<!-- M365 Agents SDK -->
|
||||
<PackageVersion Include="AdaptiveCards" Version="3.1.0" />
|
||||
|
||||
@@ -12,7 +12,9 @@ static Task<PermissionRequestResult> PromptPermission(PermissionRequest request,
|
||||
Console.Write("Approve? (y/n): ");
|
||||
|
||||
string? input = Console.ReadLine()?.Trim().ToUpperInvariant();
|
||||
string kind = input is "Y" or "YES" ? "approved" : "denied-interactively-by-user";
|
||||
PermissionRequestResultKind kind = input is "Y" or "YES"
|
||||
? PermissionRequestResultKind.Approved
|
||||
: PermissionRequestResultKind.Rejected;
|
||||
|
||||
return Task.FromResult(new PermissionRequestResult { Kind = kind });
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
|
||||
string prompt = string.Join("\n", messages.Select(m => m.Text));
|
||||
|
||||
// Handle DataContent as attachments
|
||||
(List<UserMessageDataAttachmentsItem>? attachments, tempDir) = await ProcessDataContentAttachmentsAsync(
|
||||
(List<UserMessageAttachmentFile>? attachments, tempDir) = await ProcessDataContentAttachmentsAsync(
|
||||
messages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -443,11 +443,11 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
|
||||
return new SessionConfig { Tools = mappedTools, SystemMessage = systemMessage };
|
||||
}
|
||||
|
||||
private static async Task<(List<UserMessageDataAttachmentsItem>? Attachments, string? TempDir)> ProcessDataContentAttachmentsAsync(
|
||||
private static async Task<(List<UserMessageAttachmentFile>? Attachments, string? TempDir)> ProcessDataContentAttachmentsAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
List<UserMessageDataAttachmentsItem>? attachments = null;
|
||||
List<UserMessageAttachmentFile>? attachments = null;
|
||||
string? tempDir = null;
|
||||
foreach (ChatMessage message in messages)
|
||||
{
|
||||
@@ -461,7 +461,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
|
||||
string tempFilePath = await dataContent.SaveToAsync(tempDir, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
attachments ??= [];
|
||||
attachments.Add(new UserMessageDataAttachmentsItemFile
|
||||
attachments.Add(new UserMessageAttachmentFile
|
||||
{
|
||||
Path = tempFilePath,
|
||||
DisplayName = Path.GetFileName(tempFilePath)
|
||||
|
||||
+5
-7
@@ -14,7 +14,7 @@ public class GitHubCopilotAgentTests
|
||||
private const string SkipReason = "Integration tests require GitHub Copilot CLI installed. For local execution only.";
|
||||
|
||||
private static Task<PermissionRequestResult> OnPermissionRequestAsync(PermissionRequest request, PermissionInvocation invocation)
|
||||
=> Task.FromResult(new PermissionRequestResult { Kind = "approved" });
|
||||
=> Task.FromResult(new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved });
|
||||
|
||||
[Fact(Skip = SkipReason)]
|
||||
public async Task RunAsync_WithSimplePrompt_ReturnsResponseAsync()
|
||||
@@ -201,11 +201,10 @@ public class GitHubCopilotAgentTests
|
||||
SessionConfig sessionConfig = new()
|
||||
{
|
||||
OnPermissionRequest = OnPermissionRequestAsync,
|
||||
McpServers = new Dictionary<string, object>
|
||||
McpServers = new Dictionary<string, McpServerConfig>
|
||||
{
|
||||
["filesystem"] = new McpLocalServerConfig
|
||||
["filesystem"] = new McpStdioServerConfig
|
||||
{
|
||||
Type = "stdio",
|
||||
Command = "npx",
|
||||
Args = ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
Tools = ["*"],
|
||||
@@ -234,11 +233,10 @@ public class GitHubCopilotAgentTests
|
||||
SessionConfig sessionConfig = new()
|
||||
{
|
||||
OnPermissionRequest = OnPermissionRequestAsync,
|
||||
McpServers = new Dictionary<string, object>
|
||||
McpServers = new Dictionary<string, McpServerConfig>
|
||||
{
|
||||
["microsoft-learn"] = new McpRemoteServerConfig
|
||||
["microsoft-learn"] = new McpHttpServerConfig
|
||||
{
|
||||
Type = "http",
|
||||
Url = "https://learn.microsoft.com/api/mcp",
|
||||
Tools = ["*"],
|
||||
},
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ public sealed class GitHubCopilotAgentTests
|
||||
var systemMessage = new SystemMessageConfig { Mode = SystemMessageMode.Append, Content = "Be helpful" };
|
||||
PermissionRequestHandler permissionHandler = (_, _) => Task.FromResult(new PermissionRequestResult());
|
||||
UserInputHandler userInputHandler = (_, _) => Task.FromResult(new UserInputResponse { Answer = "input" });
|
||||
var mcpServers = new Dictionary<string, object> { ["server1"] = new McpLocalServerConfig() };
|
||||
var mcpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig() };
|
||||
|
||||
var source = new SessionConfig
|
||||
{
|
||||
@@ -162,7 +162,7 @@ public sealed class GitHubCopilotAgentTests
|
||||
var systemMessage = new SystemMessageConfig { Mode = SystemMessageMode.Append, Content = "Be helpful" };
|
||||
PermissionRequestHandler permissionHandler = (_, _) => Task.FromResult(new PermissionRequestResult());
|
||||
UserInputHandler userInputHandler = (_, _) => Task.FromResult(new UserInputResponse { Answer = "input" });
|
||||
var mcpServers = new Dictionary<string, object> { ["server1"] = new McpLocalServerConfig() };
|
||||
var mcpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig() };
|
||||
|
||||
var source = new SessionConfig
|
||||
{
|
||||
|
||||
@@ -216,10 +216,12 @@ class AnthropicSettings(TypedDict, total=False):
|
||||
Keys:
|
||||
api_key: The Anthropic API key.
|
||||
chat_model: The Anthropic chat model.
|
||||
base_url: Optional base URL for the Anthropic API endpoint.
|
||||
"""
|
||||
|
||||
api_key: SecretString | None
|
||||
chat_model: str | None
|
||||
base_url: str | None
|
||||
|
||||
|
||||
class RawAnthropicClient(
|
||||
@@ -248,6 +250,7 @@ class RawAnthropicClient(
|
||||
*,
|
||||
api_key: str | None = None,
|
||||
model: str | None = None,
|
||||
base_url: str | None = None,
|
||||
anthropic_client: AnthropicAsyncClient | None = None,
|
||||
additional_beta_flags: list[str] | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
@@ -259,6 +262,8 @@ class RawAnthropicClient(
|
||||
Keyword Args:
|
||||
api_key: The Anthropic API key to use for authentication.
|
||||
model: The model to use.
|
||||
base_url: Optional base URL for the Anthropic API endpoint. Useful for Foundry or
|
||||
other compatible deployments. Falls back to ``ANTHROPIC_BASE_URL`` env variable.
|
||||
anthropic_client: An existing Anthropic client to use. If not provided, one will be created.
|
||||
This can be used to further configure the client before passing it in.
|
||||
For instance if you need to set a different base_url for testing or private deployments.
|
||||
@@ -284,6 +289,13 @@ class RawAnthropicClient(
|
||||
api_key="your_anthropic_api_key",
|
||||
)
|
||||
|
||||
# Or with a custom base URL (e.g. for Foundry-compatible endpoints)
|
||||
client = RawAnthropicClient(
|
||||
model="claude-sonnet-4-5-20250929",
|
||||
api_key="your_anthropic_api_key",
|
||||
base_url="https://custom-anthropic-endpoint.com",
|
||||
)
|
||||
|
||||
# Or loading from a .env file
|
||||
client = RawAnthropicClient(env_file_path="path/to/.env")
|
||||
|
||||
@@ -316,12 +328,14 @@ class RawAnthropicClient(
|
||||
env_prefix="ANTHROPIC_",
|
||||
api_key=api_key,
|
||||
chat_model=model,
|
||||
base_url=base_url,
|
||||
env_file_path=env_file_path,
|
||||
env_file_encoding=env_file_encoding,
|
||||
)
|
||||
|
||||
api_key_secret = anthropic_settings.get("api_key")
|
||||
model_setting = anthropic_settings.get("chat_model")
|
||||
base_url_setting = anthropic_settings.get("base_url")
|
||||
|
||||
if anthropic_client is None:
|
||||
if api_key_secret is None:
|
||||
@@ -332,6 +346,7 @@ class RawAnthropicClient(
|
||||
|
||||
anthropic_client = AsyncAnthropic(
|
||||
api_key=api_key_secret.get_secret_value(),
|
||||
base_url=base_url_setting,
|
||||
default_headers={"User-Agent": get_user_agent()},
|
||||
)
|
||||
|
||||
@@ -1409,6 +1424,7 @@ class AnthropicClient(
|
||||
*,
|
||||
api_key: str | None = None,
|
||||
model: str | None = None,
|
||||
base_url: str | None = None,
|
||||
anthropic_client: AnthropicAsyncClient | None = None,
|
||||
additional_beta_flags: list[str] | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
@@ -1422,6 +1438,8 @@ class AnthropicClient(
|
||||
Keyword Args:
|
||||
api_key: The Anthropic API key to use for authentication.
|
||||
model: The model to use.
|
||||
base_url: Optional base URL for the Anthropic API endpoint. Useful for Foundry or
|
||||
other compatible deployments. Falls back to ``ANTHROPIC_BASE_URL`` env variable.
|
||||
anthropic_client: An existing Anthropic client to use. If not provided, one will be created.
|
||||
This can be used to further configure the client before passing it in.
|
||||
For instance if you need to set a different base_url for testing or private deployments.
|
||||
@@ -1448,6 +1466,13 @@ class AnthropicClient(
|
||||
api_key="your_anthropic_api_key",
|
||||
)
|
||||
|
||||
# Or with a custom base URL (e.g. for Foundry-compatible endpoints)
|
||||
client = AnthropicClient(
|
||||
model="claude-sonnet-4-5-20250929",
|
||||
api_key="your_anthropic_api_key",
|
||||
base_url="https://custom-anthropic-endpoint.com",
|
||||
)
|
||||
|
||||
# Or loading from a .env file
|
||||
client = AnthropicClient(env_file_path="path/to/.env")
|
||||
|
||||
@@ -1477,6 +1502,7 @@ class AnthropicClient(
|
||||
super().__init__(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
anthropic_client=anthropic_client,
|
||||
additional_beta_flags=additional_beta_flags,
|
||||
additional_properties=additional_properties,
|
||||
|
||||
@@ -149,6 +149,108 @@ def test_anthropic_client_init_auto_create_client(
|
||||
assert client.model == anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"]
|
||||
|
||||
|
||||
def test_anthropic_client_init_with_base_url(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test AnthropicClient accepts a base_url and passes it to the underlying AsyncAnthropic client."""
|
||||
custom_url = "https://custom-anthropic-endpoint.com"
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
assert custom_url in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
def test_raw_anthropic_client_init_with_base_url(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test RawAnthropicClient accepts a base_url and passes it to the underlying AsyncAnthropic client."""
|
||||
custom_url = "https://custom-anthropic-endpoint.com"
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
assert custom_url in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_anthropic_client_init_base_url_from_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test AnthropicClient picks up base_url from ANTHROPIC_BASE_URL env variable when not passed explicitly."""
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
)
|
||||
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_raw_anthropic_client_init_base_url_from_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test RawAnthropicClient picks up base_url from ANTHROPIC_BASE_URL env variable when not passed explicitly."""
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
)
|
||||
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_anthropic_client_init_explicit_base_url_wins_over_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that an explicit base_url kwarg takes priority over ANTHROPIC_BASE_URL env variable."""
|
||||
explicit_url = "https://explicit-endpoint.example.com"
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=explicit_url,
|
||||
)
|
||||
|
||||
assert explicit_url in str(client.anthropic_client.base_url)
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] not in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_raw_anthropic_client_init_explicit_base_url_wins_over_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that an explicit base_url kwarg takes priority over ANTHROPIC_BASE_URL env variable."""
|
||||
explicit_url = "https://explicit-endpoint.example.com"
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=explicit_url,
|
||||
)
|
||||
|
||||
assert explicit_url in str(client.anthropic_client.base_url)
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] not in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
def test_anthropic_client_init_missing_api_key() -> None:
|
||||
"""Test AnthropicClient initialization when API key is missing."""
|
||||
with patch("agent_framework_anthropic._chat_client.load_settings") as mock_load:
|
||||
|
||||
@@ -352,8 +352,8 @@ __all__ = [
|
||||
"ContinuationToken",
|
||||
"ConversationSplit",
|
||||
"ConversationSplitter",
|
||||
"Default",
|
||||
"DeduplicatingSkillsSource",
|
||||
"Default",
|
||||
"DelegatingSkillsSource",
|
||||
"Edge",
|
||||
"EdgeCondition",
|
||||
|
||||
@@ -446,14 +446,10 @@ class FileSkillScript(SkillScript):
|
||||
"""
|
||||
if not isinstance(skill, FileSkill):
|
||||
raise TypeError(
|
||||
f"File-based script '{self.name}' requires a FileSkill "
|
||||
f"but received '{type(skill).__name__}'."
|
||||
f"File-based script '{self.name}' requires a FileSkill but received '{type(skill).__name__}'."
|
||||
)
|
||||
if self._runner is None:
|
||||
raise ValueError(
|
||||
f"Script '{self.name}' requires a runner. "
|
||||
"Provide a script_runner for file-based scripts."
|
||||
)
|
||||
raise ValueError(f"Script '{self.name}' requires a runner. Provide a script_runner for file-based scripts.")
|
||||
result = self._runner(skill, self, args)
|
||||
if inspect.isawaitable(result):
|
||||
return await result
|
||||
@@ -570,8 +566,7 @@ def _validate_skill_description(name: str, description: str) -> None:
|
||||
raise ValueError("Skill description cannot be empty.")
|
||||
if len(description) > MAX_DESCRIPTION_LENGTH:
|
||||
raise ValueError(
|
||||
f"Skill '{name}' has an invalid description: "
|
||||
f"Must be {MAX_DESCRIPTION_LENGTH} characters or fewer."
|
||||
f"Skill '{name}' has an invalid description: Must be {MAX_DESCRIPTION_LENGTH} characters or fewer."
|
||||
)
|
||||
|
||||
|
||||
@@ -1993,10 +1988,7 @@ class FileSkillsSource(SkillsSource):
|
||||
raise ValueError(f"Resource file '{resource_name}' not found in skill directory '{skill_dir}'.")
|
||||
|
||||
if FileSkillsSource._has_symlink_in_path(resource_full_path, root_directory_path):
|
||||
raise ValueError(
|
||||
f"Resource file '{resource_name}' "
|
||||
"has a symlink in its path; symlinks are not allowed."
|
||||
)
|
||||
raise ValueError(f"Resource file '{resource_name}' has a symlink in its path; symlinks are not allowed.")
|
||||
|
||||
return resource_full_path
|
||||
|
||||
|
||||
@@ -1190,7 +1190,9 @@ class TestSkillsProviderCodeSkill:
|
||||
|
||||
provider = SkillsProvider([skill])
|
||||
await _init_provider(provider)
|
||||
result = await provider._read_skill_resource(_raw_skills(provider), "prog-skill", "get_user_data", auth_token="abc")
|
||||
result = await provider._read_skill_resource(
|
||||
_raw_skills(provider), "prog-skill", "get_user_data", auth_token="abc"
|
||||
)
|
||||
assert result == "data with token=abc"
|
||||
|
||||
async def test_read_callable_resource_without_kwargs_ignores_extra_args(self) -> None:
|
||||
@@ -2059,6 +2061,7 @@ class TestSkillResourceRead:
|
||||
|
||||
async def test_read_async_function(self) -> None:
|
||||
"""read() awaits an async function and returns its result."""
|
||||
|
||||
async def get_data() -> str:
|
||||
return "async result"
|
||||
|
||||
@@ -2068,6 +2071,7 @@ class TestSkillResourceRead:
|
||||
|
||||
async def test_read_function_with_kwargs(self) -> None:
|
||||
"""read() forwards kwargs to functions that accept them."""
|
||||
|
||||
def get_config(**kwargs: Any) -> str:
|
||||
return f"user={kwargs.get('user_id')}"
|
||||
|
||||
@@ -2077,6 +2081,7 @@ class TestSkillResourceRead:
|
||||
|
||||
async def test_read_async_function_with_kwargs(self) -> None:
|
||||
"""read() forwards kwargs to async functions that accept them."""
|
||||
|
||||
async def get_config(**kwargs: Any) -> str:
|
||||
return f"user={kwargs.get('user_id')}"
|
||||
|
||||
@@ -2086,6 +2091,7 @@ class TestSkillResourceRead:
|
||||
|
||||
async def test_read_function_without_kwargs_ignores_extra(self) -> None:
|
||||
"""read() does not pass kwargs to functions that don't accept them."""
|
||||
|
||||
def simple() -> str:
|
||||
return "fixed"
|
||||
|
||||
@@ -2095,6 +2101,7 @@ class TestSkillResourceRead:
|
||||
|
||||
async def test_read_function_raises_propagates(self) -> None:
|
||||
"""read() propagates exceptions from the function."""
|
||||
|
||||
def failing() -> str:
|
||||
raise RuntimeError("boom")
|
||||
|
||||
@@ -2747,6 +2754,7 @@ class TestSkillsProviderFactories:
|
||||
|
||||
async def test_code_script_returns_object(self) -> None:
|
||||
"""Code-defined scripts can return non-string objects."""
|
||||
|
||||
def returns_dict() -> dict:
|
||||
return {"status": "ok", "value": 42}
|
||||
|
||||
@@ -2855,8 +2863,8 @@ class TestSkillsProviderFactories:
|
||||
|
||||
provider = SkillsProvider([skill])
|
||||
await _init_provider(provider)
|
||||
result = await provider._run_skill_script(_raw_skills(provider),
|
||||
"my-skill", "process", args={"mode": "llm-value"}, mode="runtime-value"
|
||||
result = await provider._run_skill_script(
|
||||
_raw_skills(provider), "my-skill", "process", args={"mode": "llm-value"}, mode="runtime-value"
|
||||
)
|
||||
assert "Error" in result
|
||||
|
||||
@@ -2946,6 +2954,7 @@ class TestSkillsProviderFactories:
|
||||
|
||||
async def test_code_script_exception_returns_error(self) -> None:
|
||||
"""A code script function that raises should return an error string."""
|
||||
|
||||
def failing_script() -> str:
|
||||
raise RuntimeError("Something went wrong")
|
||||
|
||||
@@ -3170,6 +3179,7 @@ class TestLoadSkillWithScripts:
|
||||
|
||||
async def test_code_skill_scripts_element_contains_parameters(self) -> None:
|
||||
"""Scripts XML includes parameters schema when the function has typed parameters."""
|
||||
|
||||
def analyze(query: str, limit: int = 10) -> str:
|
||||
return "result"
|
||||
|
||||
@@ -3755,9 +3765,7 @@ class TestSourceComposition:
|
||||
)
|
||||
(skill_dir / "run.py").write_text("print('hi')", encoding="utf-8")
|
||||
|
||||
source = DeduplicatingSkillsSource(
|
||||
FileSkillsSource(str(tmp_path), script_runner=_noop_script_runner)
|
||||
)
|
||||
source = DeduplicatingSkillsSource(FileSkillsSource(str(tmp_path), script_runner=_noop_script_runner))
|
||||
provider = SkillsProvider(source)
|
||||
await _init_provider(provider)
|
||||
assert "my-skill" in _ctx(provider)[0]
|
||||
@@ -3798,9 +3806,7 @@ class TestSourceComposition:
|
||||
call_log.append("source")
|
||||
return "source"
|
||||
|
||||
source = DeduplicatingSkillsSource(
|
||||
FileSkillsSource(str(tmp_path), script_runner=source_runner)
|
||||
)
|
||||
source = DeduplicatingSkillsSource(FileSkillsSource(str(tmp_path), script_runner=source_runner))
|
||||
provider = SkillsProvider(source)
|
||||
await _init_provider(provider)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user