mirror of https://github.com/microsoft/autogen.git
* update * fix 2660 * remove unnecessary feed
This commit is contained in:
parent
5f7c34ac72
commit
ba82c1d566
|
@ -33,7 +33,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral", "src\Auto
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.Mistral.Tests", "test\AutoGen.Mistral.Tests\AutoGen.Mistral.Tests.csproj", "{15441693-3659-4868-B6C1-B106F52FF3BA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoGen.SemanticKernel.Tests", "test\AutoGen.SemanticKernel.Tests\AutoGen.SemanticKernel.Tests.csproj", "{1DFABC4A-8458-4875-8DCB-59F3802DAC65}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoGen.DotnetInteractive.Tests", "test\AutoGen.DotnetInteractive.Tests\AutoGen.DotnetInteractive.Tests.csproj", "{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -93,6 +95,10 @@ Global
|
|||
{1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1DFABC4A-8458-4875-8DCB-59F3802DAC65}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -111,6 +117,7 @@ Global
|
|||
{6585D1A4-3D97-4D76-A688-1933B61AEB19} = {18BF8DD7-0585-48BF-8F97-AD333080CE06}
|
||||
{15441693-3659-4868-B6C1-B106F52FF3BA} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
|
||||
{1DFABC4A-8458-4875-8DCB-59F3802DAC65} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
|
||||
{B61388CA-DC73-4B7F-A7B2-7B9A86C9229E} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
|
||||
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
|
||||
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<disabledPackageSources />
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<FluentAssertionVersion>6.8.0</FluentAssertionVersion>
|
||||
<XUnitVersion>2.4.2</XUnitVersion>
|
||||
<MicrosoftNETTestSdkVersion>17.7.0</MicrosoftNETTestSdkVersion>
|
||||
<MicrosoftDotnetInteractive>1.0.0-beta.23523.2</MicrosoftDotnetInteractive>
|
||||
<MicrosoftDotnetInteractive>1.0.0-beta.24229.4</MicrosoftDotnetInteractive>
|
||||
<MicrosoftSourceLinkGitHubVersion>8.0.0</MicrosoftSourceLinkGitHubVersion>
|
||||
<JsonSchemaVersion>4.0.0</JsonSchemaVersion>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.DotNet.Interactive.VisualStudio" Version="$(MicrosoftDotnetInteractive)" />
|
||||
<PackageReference Include="Microsoft.DotNet.Interactive" Version="$(MicrosoftDotnetInteractive)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -27,14 +27,12 @@
|
|||
<EmbeddedResource Include="RestoreInteractive.config" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="$(AzureOpenAIVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AutoGen\AutoGen.csproj" />
|
||||
<ProjectReference Include="..\AutoGen.Core\AutoGen.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -12,57 +12,58 @@ namespace AutoGen.DotnetInteractive;
|
|||
public class DotnetInteractiveFunction : IDisposable
|
||||
{
|
||||
private readonly InteractiveService? _interactiveService = null;
|
||||
private string? _notebookPath;
|
||||
private string _notebookPath;
|
||||
private readonly KernelInfoCollection _kernelInfoCollection = new KernelInfoCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="DotnetInteractiveFunction"/>"
|
||||
/// </summary>
|
||||
/// <param name="interactiveService">interactive service to use.</param>
|
||||
/// <param name="notebookPath">notebook path if provided.</param>
|
||||
public DotnetInteractiveFunction(InteractiveService interactiveService, string? notebookPath = null, bool continueFromExistingNotebook = false)
|
||||
{
|
||||
this._interactiveService = interactiveService;
|
||||
this._notebookPath = notebookPath;
|
||||
this._notebookPath = notebookPath ?? Path.GetTempPath() + "notebook.ipynb";
|
||||
this._kernelInfoCollection.Add(new KernelInfo("csharp"));
|
||||
this._kernelInfoCollection.Add(new KernelInfo("markdown"));
|
||||
|
||||
if (this._notebookPath != null)
|
||||
if (continueFromExistingNotebook == false)
|
||||
{
|
||||
if (continueFromExistingNotebook == false)
|
||||
// remove existing notebook
|
||||
if (File.Exists(this._notebookPath))
|
||||
{
|
||||
// remove existing notebook
|
||||
if (File.Exists(this._notebookPath))
|
||||
{
|
||||
File.Delete(this._notebookPath);
|
||||
}
|
||||
|
||||
var document = new InteractiveDocument();
|
||||
|
||||
using var stream = File.OpenWrite(_notebookPath);
|
||||
Notebook.Write(document, stream, this._kernelInfoCollection);
|
||||
stream.Flush();
|
||||
stream.Dispose();
|
||||
File.Delete(this._notebookPath);
|
||||
}
|
||||
else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath))
|
||||
|
||||
var document = new InteractiveDocument();
|
||||
|
||||
using var stream = File.OpenWrite(_notebookPath);
|
||||
Notebook.Write(document, stream, this._kernelInfoCollection);
|
||||
stream.Flush();
|
||||
stream.Dispose();
|
||||
}
|
||||
else if (continueFromExistingNotebook == true && File.Exists(this._notebookPath))
|
||||
{
|
||||
// load existing notebook
|
||||
using var readStream = File.OpenRead(this._notebookPath);
|
||||
var document = Notebook.Read(readStream, this._kernelInfoCollection);
|
||||
foreach (var cell in document.Elements)
|
||||
{
|
||||
// load existing notebook
|
||||
using var readStream = File.OpenRead(this._notebookPath);
|
||||
var document = Notebook.Read(readStream, this._kernelInfoCollection);
|
||||
foreach (var cell in document.Elements)
|
||||
if (cell.KernelName == "csharp")
|
||||
{
|
||||
if (cell.KernelName == "csharp")
|
||||
{
|
||||
var code = cell.Contents;
|
||||
this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait();
|
||||
}
|
||||
var code = cell.Contents;
|
||||
this._interactiveService.SubmitCSharpCodeAsync(code, default).Wait();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create an empty notebook
|
||||
var document = new InteractiveDocument();
|
||||
}
|
||||
else
|
||||
{
|
||||
// create an empty notebook
|
||||
var document = new InteractiveDocument();
|
||||
|
||||
using var stream = File.OpenWrite(_notebookPath);
|
||||
Notebook.Write(document, stream, this._kernelInfoCollection);
|
||||
stream.Flush();
|
||||
stream.Dispose();
|
||||
}
|
||||
using var stream = File.OpenWrite(_notebookPath);
|
||||
Notebook.Write(document, stream, this._kernelInfoCollection);
|
||||
stream.Flush();
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ using System.Diagnostics;
|
|||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.DotNet.Interactive;
|
||||
using Microsoft.DotNet.Interactive.App.Connection;
|
||||
using Microsoft.DotNet.Interactive.Commands;
|
||||
using Microsoft.DotNet.Interactive.Connection;
|
||||
using Microsoft.DotNet.Interactive.Events;
|
||||
|
@ -41,7 +40,7 @@ public class InteractiveService : IDisposable
|
|||
|
||||
public async Task<bool> StartAsync(string workingDirectory, CancellationToken ct = default)
|
||||
{
|
||||
this.kernel = await this.CreateKernelAsync(workingDirectory, ct);
|
||||
this.kernel = await this.CreateKernelAsync(workingDirectory, true, ct);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -84,7 +83,51 @@ public class InteractiveService : IDisposable
|
|||
return await this.SubmitCommandAsync(command, ct);
|
||||
}
|
||||
|
||||
private async Task<Kernel> CreateKernelAsync(string workingDirectory, CancellationToken ct = default)
|
||||
public bool RestoreDotnetInteractive()
|
||||
{
|
||||
this.WriteLine("Restore dotnet interactive tool");
|
||||
// write RestoreInteractive.config from embedded resource to this.workingDirectory
|
||||
var assembly = Assembly.GetAssembly(typeof(InteractiveService))!;
|
||||
var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config";
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName)!)
|
||||
using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config")))
|
||||
{
|
||||
stream.CopyTo(fileStream);
|
||||
}
|
||||
|
||||
// write dotnet-tool.json from embedded resource to this.workingDirectory
|
||||
|
||||
resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json";
|
||||
using (var stream2 = assembly.GetManifestResourceStream(resourceName)!)
|
||||
using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json")))
|
||||
{
|
||||
stream2.CopyTo(fileStream2);
|
||||
}
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"tool restore --configfile RestoreInteractive.config",
|
||||
WorkingDirectory = this.installingDirectory,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
|
||||
using var process = new Process { StartInfo = psi };
|
||||
process.OutputDataReceived += this.PrintProcessOutput;
|
||||
process.ErrorDataReceived += this.PrintProcessOutput;
|
||||
process.Start();
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
return process.ExitCode == 0;
|
||||
}
|
||||
|
||||
private async Task<Kernel> CreateKernelAsync(string workingDirectory, bool restoreWhenFail = true, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -139,13 +182,13 @@ public class InteractiveService : IDisposable
|
|||
|
||||
return compositeKernel;
|
||||
}
|
||||
catch (CommandLineInvocationException ex) when (ex.Message.Contains("Cannot find a tool in the manifest file that has a command named 'dotnet-interactive'"))
|
||||
catch (CommandLineInvocationException) when (restoreWhenFail)
|
||||
{
|
||||
var success = this.RestoreDotnetInteractive();
|
||||
|
||||
if (success)
|
||||
{
|
||||
return await this.CreateKernelAsync(workingDirectory, ct);
|
||||
return await this.CreateKernelAsync(workingDirectory, false, ct);
|
||||
}
|
||||
|
||||
throw;
|
||||
|
@ -176,50 +219,6 @@ public class InteractiveService : IDisposable
|
|||
this.Output?.Invoke(this, data);
|
||||
}
|
||||
|
||||
private bool RestoreDotnetInteractive()
|
||||
{
|
||||
this.WriteLine("Restore dotnet interactive tool");
|
||||
// write RestoreInteractive.config from embedded resource to this.workingDirectory
|
||||
var assembly = Assembly.GetAssembly(typeof(InteractiveService))!;
|
||||
var resourceName = "AutoGen.DotnetInteractive.RestoreInteractive.config";
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName)!)
|
||||
using (var fileStream = File.Create(Path.Combine(this.installingDirectory, "RestoreInteractive.config")))
|
||||
{
|
||||
stream.CopyTo(fileStream);
|
||||
}
|
||||
|
||||
// write dotnet-tool.json from embedded resource to this.workingDirectory
|
||||
|
||||
resourceName = "AutoGen.DotnetInteractive.dotnet-tools.json";
|
||||
using (var stream2 = assembly.GetManifestResourceStream(resourceName)!)
|
||||
using (var fileStream2 = File.Create(Path.Combine(this.installingDirectory, "dotnet-tools.json")))
|
||||
{
|
||||
stream2.CopyTo(fileStream2);
|
||||
}
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"tool restore --configfile RestoreInteractive.config",
|
||||
WorkingDirectory = this.installingDirectory,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
|
||||
using var process = new Process { StartInfo = psi };
|
||||
process.OutputDataReceived += this.PrintProcessOutput;
|
||||
process.ErrorDataReceived += this.PrintProcessOutput;
|
||||
process.Start();
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
return process.ExitCode == 0;
|
||||
}
|
||||
|
||||
private void PrintProcessOutput(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"isRoot": true,
|
||||
"tools": {
|
||||
"Microsoft.dotnet-interactive": {
|
||||
"version": "1.0.431302",
|
||||
"version": "1.0.522904",
|
||||
"commands": [
|
||||
"dotnet-interactive"
|
||||
]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TestTargetFramework)</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ApprovalTests" Version="$(ApprovalTestVersion)" />
|
||||
<PackageReference Include="FluentAssertions" Version="$(FluentAssertionVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XUnitVersion)" />
|
||||
<PackageReference Include="xunit.runner.console" Version="$(XUnitVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AutoGen.SourceGenerator\AutoGen.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\AutoGen.Tests\AutoGen.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// DotnetInteractiveServiceTest.cs
|
||||
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace AutoGen.DotnetInteractive.Tests;
|
||||
|
||||
public class DotnetInteractiveServiceTest : IDisposable
|
||||
{
|
||||
private ITestOutputHelper _output;
|
||||
private InteractiveService _interactiveService;
|
||||
private string _workingDir;
|
||||
|
||||
public DotnetInteractiveServiceTest(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
_workingDir = Path.Combine(Path.GetTempPath(), "test", Path.GetRandomFileName());
|
||||
if (!Directory.Exists(_workingDir))
|
||||
{
|
||||
Directory.CreateDirectory(_workingDir);
|
||||
}
|
||||
|
||||
_interactiveService = new InteractiveService(_workingDir);
|
||||
_interactiveService.StartAsync(_workingDir, default).Wait();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_interactiveService.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ItRunCSharpCodeSnippetTestsAsync()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
var isRunning = await _interactiveService.StartAsync(_workingDir, cts.Token);
|
||||
|
||||
isRunning.Should().BeTrue();
|
||||
|
||||
_interactiveService.IsRunning().Should().BeTrue();
|
||||
|
||||
// test code snippet
|
||||
var hello_world = @"
|
||||
Console.WriteLine(""hello world"");
|
||||
";
|
||||
|
||||
await this.TestCSharpCodeSnippet(_interactiveService, hello_world, "hello world");
|
||||
await this.TestCSharpCodeSnippet(
|
||||
_interactiveService,
|
||||
code: @"
|
||||
Console.WriteLine(""hello world""
|
||||
",
|
||||
expectedOutput: "Error: (2,32): error CS1026: ) expected");
|
||||
|
||||
await this.TestCSharpCodeSnippet(
|
||||
service: _interactiveService,
|
||||
code: "throw new Exception();",
|
||||
expectedOutput: "Error: System.Exception: Exception of type 'System.Exception' was thrown");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ItRunPowershellScriptTestsAsync()
|
||||
{
|
||||
// test power shell
|
||||
var ps = @"Write-Output ""hello world""";
|
||||
await this.TestPowershellCodeSnippet(_interactiveService, ps, "hello world");
|
||||
}
|
||||
|
||||
private async Task TestPowershellCodeSnippet(InteractiveService service, string code, string expectedOutput)
|
||||
{
|
||||
var result = await service.SubmitPowershellCodeAsync(code, CancellationToken.None);
|
||||
result.Should().StartWith(expectedOutput);
|
||||
}
|
||||
|
||||
private async Task TestCSharpCodeSnippet(InteractiveService service, string code, string expectedOutput)
|
||||
{
|
||||
var result = await service.SubmitCSharpCodeAsync(code, CancellationToken.None);
|
||||
result.Should().StartWith(expectedOutput);
|
||||
}
|
||||
}
|
|
@ -4,12 +4,6 @@
|
|||
|
||||
AutoGen.Net provides the following packages, you can choose to install one or more of them based on your needs:
|
||||
|
||||
> [!Note]
|
||||
> The `AutoGen.DotnetInteractive` has a dependency on `Microsoft.DotNet.Interactive.VisualStudio` which is not available on nuget.org. To restore the dependency, you need to add the following package source to your project:
|
||||
> ```bash
|
||||
> https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
|
||||
> ```
|
||||
|
||||
- `AutoGen`: The one-in-all package. This package has dependencies over `AutoGen.Core`, `AutoGen.OpenAI`, `AutoGen.LMStudio`, `AutoGen.SemanticKernel` and `AutoGen.SourceGenerator`.
|
||||
- `AutoGen.Core`: The core package, this package provides the abstraction for message type, agent and group chat.
|
||||
- `AutoGen.OpenAI`: This package provides the integration agents over openai models.
|
||||
|
|
|
@ -19,12 +19,6 @@ For example, in data analysis scenario, agent can resolve tasks like "What is th
|
|||
## How to run dotnet code snippet?
|
||||
The built-in feature of running dotnet code snippet is provided by [dotnet-interactive](https://github.com/dotnet/interactive). To run dotnet code snippet, you need to install the following package to your project, which provides the intergraion with dotnet-interactive:
|
||||
|
||||
> [!Note]
|
||||
> The `AutoGen.DotnetInteractive` has a dependency on `Microsoft.DotNet.Interactive.VisualStudio` which is not available on nuget.org. To restore the dependency, you need to add the following package source to your project:
|
||||
> ```bash
|
||||
> https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
|
||||
> ```
|
||||
|
||||
```xml
|
||||
<PackageReference Include="AutoGen.DotnetInteractive" />
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue