committed by
GitHub
14 changed files with 405 additions and 94 deletions
@ -0,0 +1,84 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { PropertyInfo } from '@abp/ui'; |
||||
|
|
||||
|
import type { AIToolPropertyDescriptorDto } from '../../types/tools'; |
||||
|
|
||||
|
import { PropertyTable } from '@abp/ui'; |
||||
|
import { |
||||
|
Checkbox, |
||||
|
FormItemRest, |
||||
|
Input, |
||||
|
InputNumber, |
||||
|
Select, |
||||
|
} from 'ant-design-vue'; |
||||
|
|
||||
|
const props = defineProps<{ |
||||
|
model: Record<string, any>; |
||||
|
property: AIToolPropertyDescriptorDto; |
||||
|
}>(); |
||||
|
const emit = defineEmits<{ |
||||
|
(event: 'change', data: Record<string, any>): void; |
||||
|
(event: 'update:value', data: Record<string, any>): void; |
||||
|
}>(); |
||||
|
const onValueChange = (value?: any) => { |
||||
|
const prop = props.model; |
||||
|
prop[props.property.name] = value; |
||||
|
emit('change', prop); |
||||
|
emit('update:value', prop); |
||||
|
}; |
||||
|
const onPropChange = (propertyInfo: PropertyInfo) => { |
||||
|
const prop = props.model; |
||||
|
prop[props.property.name] ??= {}; |
||||
|
prop[props.property.name][propertyInfo.key] = propertyInfo.value; |
||||
|
emit('change', prop); |
||||
|
emit('update:value', prop); |
||||
|
}; |
||||
|
const onPropDelete = (propertyInfo: PropertyInfo) => { |
||||
|
const prop = props.model; |
||||
|
prop[props.property.name] ??= {}; |
||||
|
delete prop[props.property.name][propertyInfo.key]; |
||||
|
emit('change', prop); |
||||
|
emit('update:value', prop); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<InputNumber |
||||
|
v-if="property.valueType === 'Number'" |
||||
|
class="w-full" |
||||
|
:min="0" |
||||
|
:value="model[property.name]" |
||||
|
@change="onValueChange($event)" |
||||
|
/> |
||||
|
<Input |
||||
|
v-if="property.valueType === 'String'" |
||||
|
class="w-full" |
||||
|
:min="0" |
||||
|
:value="model[property.name]" |
||||
|
@change="onValueChange($event.target.value)" |
||||
|
/> |
||||
|
<Checkbox |
||||
|
v-else-if="property.valueType === 'Boolean'" |
||||
|
:checked="model[property.name] === true" |
||||
|
@change="onValueChange($event.target.checked)" |
||||
|
> |
||||
|
{{ property.displayName }} |
||||
|
</Checkbox> |
||||
|
<Select |
||||
|
v-else-if="property.valueType === 'Select'" |
||||
|
class="w-full" |
||||
|
:options="property.options" |
||||
|
:field-names="{ label: 'name', value: 'value' }" |
||||
|
:value="model[property.name]" |
||||
|
@change="onValueChange($event)" |
||||
|
/> |
||||
|
<FormItemRest v-else-if="property.valueType === 'Dictionary'"> |
||||
|
<PropertyTable |
||||
|
:data="model[property.name]" |
||||
|
@change="onPropChange" |
||||
|
@delete="onPropDelete" |
||||
|
/> |
||||
|
</FormItemRest> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1,31 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using System.Web; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.AI.Tools.Mcp; |
||||
|
|
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class DefaultMcpAuthorizationCodeProvider : IMcpAuthorizationCodeProvider, ISingletonDependency |
||||
|
{ |
||||
|
public async virtual Task<string?> HandleAsync(McpAuthorizationCodeHandleContext context) |
||||
|
{ |
||||
|
using var redirectResponse = await context.HttpClient.GetAsync(context.AuthorizationUrl, context.CancellationToken); |
||||
|
var location = redirectResponse.Headers.Location; |
||||
|
|
||||
|
if (location is not null && !string.IsNullOrEmpty(location.Query)) |
||||
|
{ |
||||
|
// Parse query string to extract "code" parameter
|
||||
|
var query = location.Query.TrimStart('?'); |
||||
|
foreach (var pair in query.Split('&')) |
||||
|
{ |
||||
|
var parts = pair.Split('=', 2); |
||||
|
if (parts.Length == 2 && parts[0] == "code") |
||||
|
{ |
||||
|
return HttpUtility.UrlDecode(parts[1]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.AI.Tools.Mcp; |
||||
|
public interface IMcpAuthorizationCodeProvider |
||||
|
{ |
||||
|
Task<string?> HandleAsync(McpAuthorizationCodeHandleContext context); |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
using System; |
||||
|
using System.Net.Http; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.AI.Tools.Mcp; |
||||
|
public class McpAuthorizationCodeHandleContext |
||||
|
{ |
||||
|
public IServiceProvider ServiceProvider { get; } |
||||
|
public HttpClient HttpClient { get; } |
||||
|
public Uri AuthorizationUrl { get; } |
||||
|
public Uri RedirectUri { get; } |
||||
|
public CancellationToken CancellationToken { get; } |
||||
|
public McpAuthorizationCodeHandleContext( |
||||
|
IServiceProvider serviceProvider, |
||||
|
HttpClient httpClient, |
||||
|
Uri authorizationUrl, |
||||
|
Uri redirectUri, |
||||
|
CancellationToken cancellationToken) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
HttpClient = httpClient; |
||||
|
AuthorizationUrl = authorizationUrl; |
||||
|
RedirectUri = redirectUri; |
||||
|
CancellationToken = cancellationToken; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue