diff --git a/.github/workflows/csharp-binding-build.yaml b/.github/workflows/csharp-binding-build.yaml new file mode 100644 index 00000000..e30210aa --- /dev/null +++ b/.github/workflows/csharp-binding-build.yaml @@ -0,0 +1,240 @@ +name: CSharp Bindings Build + +permissions: + contents: write + id-token: write + +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - packages/rust-core/charp/**/* + - .github/workflows/csharp-binding-build.yaml + +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: [self-hosted, macOS, ARM64] + target: x86_64-apple-darwin + artifact: libflappy_csharp_bindings.dylib + build: | + cargo build -r --target x86_64-apple-darwin + + - host: macos-latest + target: aarch64-apple-darwin + artifact: libflappy_csharp_bindings.dylib + build: | + cargo build -r --target aarch64-apple-darwin + + + - host: ubuntu-latest + target: x86_64-pc-windows-gnu + artifact: flappy_csharp_bindings.dll + build: | + sudo apt-get install -y gcc-mingw-w64-x86-64-win32 + cargo install cargo-xwin + cargo build -r --target x86_64-pc-windows-gnu + + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + artifact: libflappy_csharp_bindings.so + build: |- + set -e && \ + sudo apt-get update + sudo apt-get install build-essential perl pkg-config libssl-dev -y + cargo build -r --target x86_64-unknown-linux-gnu + + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + artifact: libflappy_csharp_bindings.so + build: | + apk add pkgconfig openssl-dev gcc g++ make perl + set -e + cargo build -r --target x86_64-unknown-linux-musl + + + - host: ubuntu-latest + target: aarch64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 + artifact: libflappy_csharp_bindings.so + build: |- + set -e && \ + sudo apt-get update && \ + sudo apt-get install build-essential -y && \ + sudo apt-get install perl -y && \ + sudo apt-get install pkg-config libssl-dev -y && \ + export LDFLAGS="-L/usr/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/4.8.5" && \ + export CFLAGS="-fuse-ld=lld -mcrc -B/usr/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/4.8.5 --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot" && \ + export CXXFLAGS="-fuse-ld=lld -mcrc -B/usr/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/4.8.5 --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot" && \ + cargo build -r --target aarch64-unknown-linux-gnu + + #- host: ubuntu-latest + # target: aarch64-linux-android + # artifact: target/x86_64-apple-darwin/*.so + # build: | + # cargo build --target aarch64-linux-android + # ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip packages/rust-core/nodejs/*.node + + #- host: ubuntu-latest + # target: aarch64-unknown-linux-musl + # docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + # artifact: libflappy_csharp_bindings.so + # build: |- + # set -e && \ + # apk add pkgconfig openssl-dev gcc g++ make perl && \ + # rustup target add aarch64-unknown-linux-musl && \ + # cargo build -r --target aarch64-unknow-linux-musl + + name: build and upload rust code - csharp + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v3 + - name: Install + uses: dtolnay/rust-toolchain@stable + if: ${{ !matrix.settings.docker }} + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build' + run: | + cd packages/rust-core/csharp + ${{ matrix.settings.build }} + + - name: Build + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + working-directory: packages/rust-core/csharp + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: bindings-${{ matrix.settings.target }} + path: "**/${{ matrix.settings.artifact }}" + if-no-files-found: error + + package-nuget: + needs: build + runs-on: ubuntu-latest + name: package and upload nuget package - csharp + + steps: + - uses: actions/checkout@v3 + - uses: nuget/setup-nuget@v1 + with: + nuget-api-key: ${{ secrets.GITHUB_TOKEN }} + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-x86_64-apple-darwin + path: ~/artifacts/ + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-aarch64-unknown-linux-gnu + path: ~/artifacts/ + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-aarch64-apple-darwin + path: ~/artifacts/ + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-x86_64-unknown-linux-musl + path: ~/artifacts/ + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-x86_64-unknown-linux-gnu + path: ~/artifacts/ + + - name: download artifacts + uses: actions/download-artifact@v2 + with: + name: bindings-x86_64-pc-windows-gnu + path: ~/artifacts/ + + - name: make runtimes dir + run: | + find ~/artifacts + + dcp() { + local target=$2 + local src=$1 + mkdir -p ~/nuget/runtimes/$target + cp -v ~/artifacts/packages/rust-core/target/$src ~/nuget/runtimes/$target + } + + mkdir -p ~/nuget + dcp x86_64-pc-windows-gnu/release/flappy_csharp_bindings.dll win-x64 + + dcp aarch64-apple-darwin/release/libflappy_csharp_bindings.dylib osx-arm64 + dcp x86_64-apple-darwin/release/libflappy_csharp_bindings.dylib osx-x64 + + dcp x86_64-unknown-linux-gnu/release/libflappy_csharp_bindings.so linux-x64 + dcp aarch64-unknown-linux-gnu/release/libflappy_csharp_bindings.so linux-arm64 + dcp x86_64-unknown-linux-musl/release/libflappy_csharp_bindings.so linux-musl-x64 + + #cp packages/rust-core/csharp/tools/Pleisto.Flappy.Native.csproj ~/nuget + + tar cvf ~/nuget.tar ~/nuget + gzip -9 ~/nuget.tar + + - name: build nupkg + run: | + cd packages/csharp + cp -vr ~/nuget/runtimes ./runtimes + + dotnet build -c Release + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: bindings + path: | + packages/csharp/**/*.nupkg + ~/nuget.tar.gz + if-no-files-found: error + + - name: publish nuget pkg + run: | + cd packages/csharp + find -type f | grep nupkg$ | grep -v 'symbols\.nupkg$' | while read line; do + echo "Pulish: $line" + nuget push $line \ + -Source https://api.nuget.org/v3/index.json \ + -ApiKey ${{ secrets.NUGET_TOKEN }} \ + -SkipDuplicate + done \ No newline at end of file diff --git a/.github/workflows/csharp-publish.yml b/.github/workflows/csharp-publish.yml deleted file mode 100644 index 16fce99a..00000000 --- a/.github/workflows/csharp-publish.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: CSharp Nuget Publish - -on: - workflow_dispatch: - push: - branches: [main] - paths: - - 'examples/csharp/**/*' - - 'packages/csharp/**/*' - - '.github/workflows/csharp-publish.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -defaults: - run: - working-directory: packages/csharp - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: | - 3.1 - 6.0 - 7.0 - - - uses: actions/cache@v3 - with: - path: ~/.nuget/packages - # Look to see if there is a cache hit for the corresponding requirements file - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: | - ${{ runner.os }}-nuget - - - uses: nuget/setup-nuget@v1 - with: - nuget-api-key: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/checkout@v4 - - name: dotnet publish - run: | - dotnet build -c Release - find -type f | grep nupkg$ | grep -v 'symbols\.nupkg$' | while read line; do - echo "Pulish: $line" - nuget push $line \ - -Source https://api.nuget.org/v3/index.json \ - -ApiKey ${{ secrets.NUGET_TOKEN }} \ - -SkipDuplicate - done diff --git a/.github/workflows/csharp-test.yml b/.github/workflows/csharp-test.yml index 0837e233..bea802b3 100644 --- a/.github/workflows/csharp-test.yml +++ b/.github/workflows/csharp-test.yml @@ -1,4 +1,4 @@ -name: CSharp Nuget Test +name: CSharp Nuget Test - Linux on: workflow_dispatch: @@ -12,20 +12,28 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -defaults: - run: - working-directory: packages/csharp - jobs: tests: - runs-on: ubuntu-latest + name: ${{ matrix.os }} - dotnet-${{ matrix.dotnet-version.framework }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + #- windows-latest + #- macos-latest + dotnet-version: + - framework: netcoreapp3.1 + runtime: 3.1 + - framework: net6.0 + runtime: 6.0 + - framework: net7.0 + runtime: 7.0 steps: - uses: actions/setup-dotnet@v3 with: - dotnet-version: | - 3.1 - 6.0 - 7.0 + dotnet-version: ${{ matrix.dotnet-version.runtime }} env: NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -37,8 +45,32 @@ jobs: restore-keys: | ${{ runner.os }}-nuget + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + .cargo-cache + target/ + key: ${{ matrix.os }}-cargo + + - name: Install + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: actions/checkout@v4 + + - name: build binding native + run: | + cd packages/rust-core/csharp + cargo build + - name: dotnet test - env: - OPENAI_API_KEY: - run: dotnet test -e NO_GPT_TEST=true + run: | + cd packages/csharp + mkdir -p ./Pleisto.Flappy.Test/bin/Debug/${{ matrix.dotnet-version.framework }}/ + cp ../rust-core/target/debug/*csharp* ./Pleisto.Flappy.Test/bin/Debug/${{ matrix.dotnet-version.framework }}/ + dotnet test -e NO_GPT_TEST=true --framework ${{ matrix.dotnet-version.framework }} diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/Pleisto.Flappy.Example.Law.csproj b/examples/csharp/Pleisto.Flappy.Example.Law/Pleisto.Flappy.Example.Law.csproj deleted file mode 100644 index 993e2e52..00000000 --- a/examples/csharp/Pleisto.Flappy.Example.Law/Pleisto.Flappy.Example.Law.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Exe - net7.0 - enable - - - - - - - diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/Pleisto.Flappy.Example.Resume.csproj b/examples/csharp/Pleisto.Flappy.Example.Resume/Pleisto.Flappy.Example.Resume.csproj deleted file mode 100644 index 993e2e52..00000000 --- a/examples/csharp/Pleisto.Flappy.Example.Resume/Pleisto.Flappy.Example.Resume.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Exe - net7.0 - enable - - - - - - - diff --git a/examples/csharp/Pleisto.Flappy.Example.sln b/examples/csharp/Pleisto.Flappy.Example.sln deleted file mode 100644 index 99de89af..00000000 --- a/examples/csharp/Pleisto.Flappy.Example.sln +++ /dev/null @@ -1,44 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34004.107 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A82A613A-0CFD-4333-9A3D-EE658837AB1B}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy", "..\..\packages\csharp\Pleisto.Flappy\Pleisto.Flappy.csproj", "{2A0E78B5-AEAA-45B7-A96C-5EA279C92E5D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy.Example.Law", "Pleisto.Flappy.Example.Law\Pleisto.Flappy.Example.Law.csproj", "{D8CBF59B-8DBB-4CDC-A05A-8F5BF5D58674}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy.Example.Resume", "Pleisto.Flappy.Example.Resume\Pleisto.Flappy.Example.Resume.csproj", "{9E3B9CBA-42A7-4D49-80D8-772C55CC7C98}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2A0E78B5-AEAA-45B7-A96C-5EA279C92E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A0E78B5-AEAA-45B7-A96C-5EA279C92E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A0E78B5-AEAA-45B7-A96C-5EA279C92E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A0E78B5-AEAA-45B7-A96C-5EA279C92E5D}.Release|Any CPU.Build.0 = Release|Any CPU - {D8CBF59B-8DBB-4CDC-A05A-8F5BF5D58674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D8CBF59B-8DBB-4CDC-A05A-8F5BF5D58674}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D8CBF59B-8DBB-4CDC-A05A-8F5BF5D58674}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D8CBF59B-8DBB-4CDC-A05A-8F5BF5D58674}.Release|Any CPU.Build.0 = Release|Any CPU - {9E3B9CBA-42A7-4D49-80D8-772C55CC7C98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E3B9CBA-42A7-4D49-80D8-772C55CC7C98}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E3B9CBA-42A7-4D49-80D8-772C55CC7C98}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E3B9CBA-42A7-4D49-80D8-772C55CC7C98}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {8D4156ED-5B49-4AC4-B113-9ADFDE47632D} - EndGlobalSection -EndGlobal diff --git a/examples/csharp/Pleisto.Flappy.Examples/CodeInterpreter/CodeInterpreterCase.cs b/examples/csharp/Pleisto.Flappy.Examples/CodeInterpreter/CodeInterpreterCase.cs new file mode 100644 index 00000000..293b239c --- /dev/null +++ b/examples/csharp/Pleisto.Flappy.Examples/CodeInterpreter/CodeInterpreterCase.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Logging; +using OpenAI_API; +using Pleisto.Flappy.Interfaces; +using Pleisto.Flappy.LLM; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Pleisto.Flappy.Examples.CodeInterpreter +{ + internal class CodeInterpreterCase : ExampleBase + { + public override async Task OnExecuteAsync() + { + var gpt35 = new ChatGPT(new OpenAIAPI + { + Auth = new APIAuthentication(apiKey: OpenAIApiKey), + ApiUrlFormat = "https://openai.api2d.net/{0}/{1}", + ApiVersion = "v1", + }, "gpt-3.5-turbo", null, Logger.CreateLogger()); + + var agent = new FlappyAgent(new FlappyAgentConfig + { + CodeInterpreter = new CodeInterpreterOptions + { + Env = new Dictionary + { + }, + EnableNetwork = true, + } + }, null, null, Logger.CreateLogger()); + + await agent.CallCodeInterpreter("There are some rabbits and chickens in a barn. What is the number of chickens if there are 396 legs and 150 heads in the barn?"); + } + } +} diff --git a/examples/csharp/Pleisto.Flappy.Examples/ExampleBase.cs b/examples/csharp/Pleisto.Flappy.Examples/ExampleBase.cs new file mode 100644 index 00000000..69ce5749 --- /dev/null +++ b/examples/csharp/Pleisto.Flappy.Examples/ExampleBase.cs @@ -0,0 +1,23 @@ +using McMaster.Extensions.CommandLineUtils; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace Pleisto.Flappy.Examples +{ + internal abstract class ExampleBase + { + public abstract Task OnExecuteAsync(); + + [Option("--openai-api-key", Description = "OpenAI API Key, also from environment: OPENAI_API_KEY")] + public string OpenAIApiKey { get; set; } = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); + + [Option("--openai-api-url")] + public string OpenAIApiUrl { get; set; } = "https://openai.api2d.net/{0}/{1}"; + + protected static readonly ILoggerFactory Logger = LoggerFactory.Create(builder => + { + builder.AddConsole(); + }); + } +} diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/Program.cs b/examples/csharp/Pleisto.Flappy.Examples/Law/LawCase.cs similarity index 68% rename from examples/csharp/Pleisto.Flappy.Example.Law/Program.cs rename to examples/csharp/Pleisto.Flappy.Examples/Law/LawCase.cs index 90477f2e..d816a4fd 100644 --- a/examples/csharp/Pleisto.Flappy.Example.Law/Program.cs +++ b/examples/csharp/Pleisto.Flappy.Examples/Law/LawCase.cs @@ -1,35 +1,30 @@ +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using OpenAI_API; using Pleisto.Flappy.Interfaces; using Pleisto.Flappy.LLM; +using Pleisto.Flappy.Test.Law; +using System; +using System.Threading.Tasks; -namespace Pleisto.Flappy.Test.Law +namespace Pleisto.Flappy.Examples.Law { - public static class Program + internal class LawCase : ExampleBase { - private static string OpenApiKey => Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("no environment found: OPENAI_API_KEY"); - - public static bool ConsoleRun { get; set; } = true; - - public static async Task Main() + public override async Task OnExecuteAsync() { - try + var gpt35 = new ChatGPT(new OpenAIAPI { - var gpt35 = new ChatGPT(new OpenAIAPI - { - Auth = new APIAuthentication(apiKey: OpenApiKey), - ApiUrlFormat = "https://openai.api2d.net/{0}/{1}", - ApiVersion = "v1", - }, "gpt-3.5-turbo", null) - { - DebugGPT = true - }; + Auth = new APIAuthentication(apiKey: OpenAIApiKey), + ApiUrlFormat = "https://openai.api2d.net/{0}/{1}", + ApiVersion = "v1", + }, "gpt-3.5-turbo", null, Logger.CreateLogger()); - var lawAgent = new FlappyAgent(new FlappyAgentConfig - { - LLM = gpt35, - Functions = new IFlappyFunction[] - { + var lawAgent = new FlappyAgent(new FlappyAgentConfig + { + LLM = gpt35, + Functions = new IFlappyFunction[] + { new SynthesizedFunction(new SynthesizedFunctionDefinition { Name = "getMeta", @@ -42,7 +37,7 @@ public static async Task Main() Name = "getLatestLawsuitsByPlaintiff", Description= "Get the latest lawsuits by plaintiff.", Args = new getLatestLawsuits_Args(), - ReturnType = new getMeta_Args(), + ReturnType = new getMeta_Args(), Resolve = (args) => { Console.WriteLine($"====================== getLatestLawsuitsByPlaintiff call ========================="); @@ -55,18 +50,13 @@ public static async Task Main() }); } }) - }, - }, null, null); - var data = (await lawAgent.CreateExecutePlan("找到原告为张三的最新案件并返回它的元数据")); - Console.WriteLine($"====================== Final Result ========================="); - Console.WriteLine(data.ToString()); - Console.WriteLine($"====================== Final Result Of Data ========================="); - // Console.WriteLine(JArray.Parse(data["data"].ToString())); - } - catch (Exception ex) when (ConsoleRun) - { - Console.Error.WriteLine(ex.ToString()); - } + }, + }, null, null, Logger.CreateLogger()); + var data = await lawAgent.CreateExecutePlan("Find the resume of a frontend engineer and return their metadata."); + Console.WriteLine($"====================== Final Result ========================="); + Console.WriteLine(data.ToString()); + Console.WriteLine($"====================== Final Result Of Data ========================="); + // Console.WriteLine(JArray.Parse(data["data"].ToString())); } private const string MOCK_LAWSUIT_DATA = diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/Verdict.cs b/examples/csharp/Pleisto.Flappy.Examples/Law/Models/Verdict.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Law/Verdict.cs rename to examples/csharp/Pleisto.Flappy.Examples/Law/Models/Verdict.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/getLatestLawsuits_Args.cs b/examples/csharp/Pleisto.Flappy.Examples/Law/Models/getLatestLawsuits_Args.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Law/getLatestLawsuits_Args.cs rename to examples/csharp/Pleisto.Flappy.Examples/Law/Models/getLatestLawsuits_Args.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/getMeta_Args.cs b/examples/csharp/Pleisto.Flappy.Examples/Law/Models/getMeta_Args.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Law/getMeta_Args.cs rename to examples/csharp/Pleisto.Flappy.Examples/Law/Models/getMeta_Args.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Law/getMeta_Return.cs b/examples/csharp/Pleisto.Flappy.Examples/Law/Models/getMeta_Return.cs similarity index 97% rename from examples/csharp/Pleisto.Flappy.Example.Law/getMeta_Return.cs rename to examples/csharp/Pleisto.Flappy.Examples/Law/Models/getMeta_Return.cs index f569a127..8e209b2e 100644 --- a/examples/csharp/Pleisto.Flappy.Example.Law/getMeta_Return.cs +++ b/examples/csharp/Pleisto.Flappy.Examples/Law/Models/getMeta_Return.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; +using System; using System.ComponentModel; namespace Pleisto.Flappy.Test.Law diff --git a/examples/csharp/Pleisto.Flappy.Examples/Pleisto.Flappy.Examples.csproj b/examples/csharp/Pleisto.Flappy.Examples/Pleisto.Flappy.Examples.csproj new file mode 100644 index 00000000..bca6a1ee --- /dev/null +++ b/examples/csharp/Pleisto.Flappy.Examples/Pleisto.Flappy.Examples.csproj @@ -0,0 +1,17 @@ + + + + Exe + net7.0;net6.0;netcoreapp3.1;net48;net472 + + + + + + + + + + + + diff --git a/examples/csharp/Pleisto.Flappy.Examples/Program.cs b/examples/csharp/Pleisto.Flappy.Examples/Program.cs new file mode 100644 index 00000000..d3e055ea --- /dev/null +++ b/examples/csharp/Pleisto.Flappy.Examples/Program.cs @@ -0,0 +1,35 @@ +using McMaster.Extensions.CommandLineUtils; +using Pleisto.Flappy.Examples.CodeInterpreter; +using Pleisto.Flappy.Examples.Law; +using Pleisto.Flappy.Examples.Resume; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Pleisto.Flappy.Test")] + +namespace Pleisto.Flappy.Examples +{ + [Subcommand(typeof(LawCase))] + [Subcommand(typeof(ResumeCase))] + [Subcommand(typeof(CodeInterpreterCase))] + public class Program + { + public static int Main(string[] args) + { + try + { + return CommandLineApplication.Execute(args); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.ToString()); + return -1; + } + } + + public void OnExecute(CommandLineApplication app) + { + app.ShowHelp(); + } + } +} diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/GetInvokeArgs.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetInvokeArgs.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Resume/GetInvokeArgs.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetInvokeArgs.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/GetMetaArgs.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetMetaArgs.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Resume/GetMetaArgs.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetMetaArgs.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/GetMetaReturn.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetMetaReturn.cs similarity index 98% rename from examples/csharp/Pleisto.Flappy.Example.Resume/GetMetaReturn.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetMetaReturn.cs index b470c313..5473afb5 100644 --- a/examples/csharp/Pleisto.Flappy.Example.Resume/GetMetaReturn.cs +++ b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/GetMetaReturn.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System; namespace Pleisto.Flappy.Test.Resume { diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnEducation.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnEducation.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnEducation.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnEducation.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnExperiences.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnExperiences.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnExperiences.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnExperiences.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnSkill.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnSkill.cs similarity index 100% rename from examples/csharp/Pleisto.Flappy.Example.Resume/MetaReturnSkill.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/Models/MetaReturnSkill.cs diff --git a/examples/csharp/Pleisto.Flappy.Example.Resume/Program.cs b/examples/csharp/Pleisto.Flappy.Examples/Resume/ResumeCase.cs similarity index 73% rename from examples/csharp/Pleisto.Flappy.Example.Resume/Program.cs rename to examples/csharp/Pleisto.Flappy.Examples/Resume/ResumeCase.cs index 2daf32a2..c87d35ac 100644 --- a/examples/csharp/Pleisto.Flappy.Example.Resume/Program.cs +++ b/examples/csharp/Pleisto.Flappy.Examples/Resume/ResumeCase.cs @@ -1,34 +1,29 @@ +using Microsoft.Extensions.Logging; using OpenAI_API; using Pleisto.Flappy.Interfaces; using Pleisto.Flappy.LLM; +using Pleisto.Flappy.Test.Resume; +using System; +using System.Threading.Tasks; -namespace Pleisto.Flappy.Test.Resume +namespace Pleisto.Flappy.Examples.Resume { - public static class Program + internal class ResumeCase : ExampleBase { - private static string OpenApiKey => Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("no environment found: OPENAI_API_KEY"); - - public static bool ConsoleRun { get; set; } = true; - - public static async Task Main() + async public override Task OnExecuteAsync() { - try + var gpt35 = new ChatGPT(new OpenAIAPI { - var gpt35 = new ChatGPT(new OpenAIAPI - { - Auth = new APIAuthentication(apiKey: OpenApiKey), - ApiUrlFormat = "https://openai.api2d.net/{0}/{1}", - ApiVersion = "v1", - }, "gpt-3.5-turbo", null) - { - DebugGPT = true - }; + Auth = new APIAuthentication(apiKey: OpenAIApiKey), + ApiUrlFormat = "https://openai.api2d.net/{0}/{1}", + ApiVersion = "v1", + }, "gpt-3.5-turbo", null, Logger.CreateLogger()); - var lawAgent = new FlappyAgent(new FlappyAgentConfig - { - LLM = gpt35, - Functions = new IFlappyFunction[] - { + var lawAgent = new FlappyAgent(new FlappyAgentConfig + { + LLM = gpt35, + Functions = new IFlappyFunction[] + { new SynthesizedFunction(new SynthesizedFunctionDefinition { Name = "getMeta", @@ -50,18 +45,13 @@ public static async Task Main() }); }) }) - }, - }, null, null); - var data = (await lawAgent.CreateExecutePlan("Find the resume of a frontend engineer and return their metadata.")); - Console.WriteLine($"====================== Final Result ========================="); - Console.WriteLine(data.ToString()); - Console.WriteLine($"====================== Final Result Of Data ========================="); - // Console.WriteLine(JArray.Parse(data["data"].ToString())); - } - catch (Exception ex) when (ConsoleRun) - { - Console.Error.WriteLine(ex.ToString()); - } + }, + }, null, null, new LoggerFactory().CreateLogger()); + var data = (await lawAgent.CreateExecutePlan("找到前端工程师的简历并返回他的元数据")); + Console.WriteLine($"====================== Final Result ========================="); + Console.WriteLine(data.ToString()); + Console.WriteLine($"====================== Final Result Of Data ========================="); + // Console.WriteLine(JArray.Parse(data["data"].ToString())); } private const string MOCK_LAWSUIT_DATA = diff --git a/flappy.code-workspace b/flappy.code-workspace index 1bd8bbf6..10ee2e61 100644 --- a/flappy.code-workspace +++ b/flappy.code-workspace @@ -22,6 +22,10 @@ "name": "@flappy/flappy-java-bindings", "path": "packages/rust-core/java" }, + { + "name": "@flappy/flappy-csharp-bindings", + "path": "packages/rust-core/csharp" + }, { "name": "@pleisto/node-flappy", "path": "packages/nodejs" diff --git a/packages/csharp/Pleisto.Flappy.Test/CodeInterpreter.cs b/packages/csharp/Pleisto.Flappy.Test/CodeInterpreter.cs new file mode 100644 index 00000000..90389613 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy.Test/CodeInterpreter.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using Pleisto.Flappy.CodeInterpreter; +using System; +using System.Collections.Generic; + +namespace Pleisto.Flappy.Tests +{ + /// + /// Test of CodeInterpreter + /// + public class CodeInterpreter + { + /// + /// Test Result of python helloworld + /// + [Test] + public void PythonHelloWorldTest() + { + const string pythonHelloWorld = @" +print('Hello World'); +"; + var result = NativeHandler.EvalPythonCode(pythonHelloWorld, false, new Dictionary + { + }); + Console.WriteLine(JObject.FromObject(result).ToString()); + Assert.That(result.StdOut.Trim(), Is.EqualTo("Hello World")); + } + + /// + /// Test result of python version + /// + [Test] + public void PythonInspect() + { + const string pythonInspectCode = @" +import platform + +print(platform.python_version()) +"; + var result = NativeHandler.EvalPythonCode(pythonInspectCode, false, new Dictionary + { + }); + Console.WriteLine(JObject.FromObject(result).ToString()); + + } + + /// + /// Test of environment + /// + [Test] + public void PythonEnvironment() + { + const string pythonEnvironment = @" +import os + +print(os.environ['testEnv']) +"; + var result = NativeHandler.EvalPythonCode(pythonEnvironment, false, new Dictionary + { + ["testEnv"] = "123123123" + }); + Console.WriteLine(JObject.FromObject(result).ToString()); + Assert.That(result.StdOut.Trim(), Is.EqualTo("123123123")); + } + + /// + /// Test of native call lib + /// + [Test] + public void NativeCall() + { + Assert.That(NativeHandler.NativeCall(), Is.EqualTo(true)); + } + } +} diff --git a/packages/csharp/Pleisto.Flappy.Test/GlobalUsings.cs b/packages/csharp/Pleisto.Flappy.Test/GlobalUsings.cs deleted file mode 100644 index 32445676..00000000 --- a/packages/csharp/Pleisto.Flappy.Test/GlobalUsings.cs +++ /dev/null @@ -1 +0,0 @@ -global using NUnit.Framework; diff --git a/packages/csharp/Pleisto.Flappy.Test/Pleisto.Flappy.Test.csproj b/packages/csharp/Pleisto.Flappy.Test/Pleisto.Flappy.Test.csproj index 45e4ee7c..b59ee8e5 100644 --- a/packages/csharp/Pleisto.Flappy.Test/Pleisto.Flappy.Test.csproj +++ b/packages/csharp/Pleisto.Flappy.Test/Pleisto.Flappy.Test.csproj @@ -1,11 +1,13 @@ - + - net7.0 - enable + net7.0;net6.0;netcoreapp3.1;net48;net472 false true + Pleisto.Flappy.Tests + + @@ -20,8 +22,7 @@ - - + diff --git a/packages/csharp/Pleisto.Flappy.Test/ProgramTest.cs b/packages/csharp/Pleisto.Flappy.Test/ProgramTest.cs index 0ad5f09c..2eab26a6 100644 --- a/packages/csharp/Pleisto.Flappy.Test/ProgramTest.cs +++ b/packages/csharp/Pleisto.Flappy.Test/ProgramTest.cs @@ -1,7 +1,8 @@ using Newtonsoft.Json.Linq; +using NUnit.Framework; using Pleisto.Flappy.Utils; -namespace Pleisto.Flappy.Test +namespace Pleisto.Flappy.Tests { public class ProgramTest { diff --git a/packages/csharp/Pleisto.Flappy.Test/TestCase.cs b/packages/csharp/Pleisto.Flappy.Test/TestCase.cs index dee3168e..97c942f5 100644 --- a/packages/csharp/Pleisto.Flappy.Test/TestCase.cs +++ b/packages/csharp/Pleisto.Flappy.Test/TestCase.cs @@ -1,7 +1,12 @@ -using LawProgram = Pleisto.Flappy.Test.Law.Program; -using ResumeProgram = Pleisto.Flappy.Test.Resume.Program; +using NUnit.Framework; +using Pleisto.Flappy.Examples; +using Pleisto.Flappy.Examples.CodeInterpreter; +using Pleisto.Flappy.Examples.Law; +using Pleisto.Flappy.Examples.Resume; +using System; +using System.Threading.Tasks; -namespace Pleisto.Flappy.Test +namespace Pleisto.Flappy.Tests { /// /// ChatGPT Test Case @@ -11,7 +16,14 @@ public class TestCase /// /// Disable GPT Test Case on environment set /// - static private bool NoGptTest => Environment.GetEnvironmentVariable("NO_GPT_TEST") == "true"; + private static bool NoGptTest => Environment.GetEnvironmentVariable("NO_GPT_TEST") == "true"; + + private async Task TestCaseBase() + where T : ExampleBase, new() + { + if (NoGptTest == false) + await new T().OnExecuteAsync(); + } /// /// Case test of Law @@ -19,13 +31,7 @@ public class TestCase /// [Test] public async Task LawTestAsync() - { - if (NoGptTest == false) - { - LawProgram.ConsoleRun = false; - await LawProgram.Main(); - } - } + => await TestCaseBase(); /// /// Case test of Law @@ -33,12 +39,14 @@ public async Task LawTestAsync() /// [Test] public async Task ResumeTestAsync() - { - if (NoGptTest == false) - { - ResumeProgram.ConsoleRun = false; - await ResumeProgram.Main(); - } - } + => await TestCaseBase(); + + /// + /// Case test of Code Interpreter + /// + /// + [Test] + public async Task CodeInterpreterTaskAsync() + => await TestCaseBase(); } } diff --git a/packages/csharp/Pleisto.Flappy.Test/launchSettings.json b/packages/csharp/Pleisto.Flappy.Test/launchSettings.json new file mode 100644 index 00000000..78e342a3 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy.Test/launchSettings.json @@ -0,0 +1,13 @@ +{ + "WSL": { + "commandName": "WSL", + "launchBrowser": true, + "launchUrl": "https://localhost:5001", + "environmentVariables": { + "ASPNETCORE_URLS": "https://localhost:5001;http://localhost:5000", + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "distributionName": "" + } + +} diff --git a/packages/csharp/Pleisto.Flappy.sln b/packages/csharp/Pleisto.Flappy.sln index 5a039125..438bb99f 100644 --- a/packages/csharp/Pleisto.Flappy.sln +++ b/packages/csharp/Pleisto.Flappy.sln @@ -13,32 +13,54 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy.Test", "Pleisto.Flappy.Test\Pleisto.Flappy.Test.csproj", "{8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy.Example.Law", "..\..\examples\csharp\Pleisto.Flappy.Example.Law\Pleisto.Flappy.Example.Law.csproj", "{52FA5991-3BD8-4CB6-B1B4-6DAAB9AA3BF2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pleisto.Flappy.Example.Resume", "..\..\examples\csharp\Pleisto.Flappy.Example.Resume\Pleisto.Flappy.Example.Resume.csproj", "{51860847-F7C2-4A00-9491-941AE2003DCB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pleisto.Flappy.Examples", "..\..\examples\csharp\Pleisto.Flappy.Examples\Pleisto.Flappy.Examples.csproj", "{9D6337DC-B6BE-46EA-803B-173EFDA51B23}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|x64.ActiveCfg = Debug|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|x64.Build.0 = Debug|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|x86.ActiveCfg = Debug|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Debug|x86.Build.0 = Debug|Any CPU {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|Any CPU.ActiveCfg = Release|Any CPU {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|Any CPU.Build.0 = Release|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|x64.ActiveCfg = Release|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|x64.Build.0 = Release|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|x86.ActiveCfg = Release|Any CPU + {40CC3C9B-8145-4F5F-96A7-D2142ABD4368}.Release|x86.Build.0 = Release|Any CPU {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|x64.Build.0 = Debug|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|x86.ActiveCfg = Debug|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Debug|x86.Build.0 = Debug|Any CPU {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|Any CPU.Build.0 = Release|Any CPU - {52FA5991-3BD8-4CB6-B1B4-6DAAB9AA3BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52FA5991-3BD8-4CB6-B1B4-6DAAB9AA3BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52FA5991-3BD8-4CB6-B1B4-6DAAB9AA3BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52FA5991-3BD8-4CB6-B1B4-6DAAB9AA3BF2}.Release|Any CPU.Build.0 = Release|Any CPU - {51860847-F7C2-4A00-9491-941AE2003DCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {51860847-F7C2-4A00-9491-941AE2003DCB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51860847-F7C2-4A00-9491-941AE2003DCB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51860847-F7C2-4A00-9491-941AE2003DCB}.Release|Any CPU.Build.0 = Release|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|x64.ActiveCfg = Release|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|x64.Build.0 = Release|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|x86.ActiveCfg = Release|Any CPU + {8DD0E5B8-03AC-4ABD-94CF-CA6EA525A05E}.Release|x86.Build.0 = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|x64.Build.0 = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Debug|x86.Build.0 = Debug|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|Any CPU.Build.0 = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|x64.ActiveCfg = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|x64.Build.0 = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|x86.ActiveCfg = Release|Any CPU + {9D6337DC-B6BE-46EA-803B-173EFDA51B23}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/packages/csharp/Pleisto.Flappy/CodeInterpreter/NativeHandler.cs b/packages/csharp/Pleisto.Flappy/CodeInterpreter/NativeHandler.cs new file mode 100644 index 00000000..8e1aa611 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/CodeInterpreter/NativeHandler.cs @@ -0,0 +1,92 @@ +using Newtonsoft.Json.Linq; +using System.Runtime.InteropServices; + +namespace Pleisto.Flappy.CodeInterpreter +{ + /// + /// Code Interpreter of Rust Handler + /// + public static class NativeHandler + { + static NativeHandler() + { + } + + #region Import of native + + /// + /// Eval Natvive Call Test + /// + /// + [DllImport("flappy_csharp_bindings", EntryPoint = "eval_native_call")] + private static extern bool EvalNativeCall(); + + /// + /// Eval python call with Json input and Json output + /// + /// + /// + [DllImport("flappy_csharp_bindings", EntryPoint = "eval_python_code_by_json")] + private static extern IntPtr EvalPythonCodeResultJson(IntPtr jsonIn); + + #endregion Import of native + + /// + /// Native call test + /// + /// + public static bool NativeCall() + { + return EvalNativeCall(); + } + + /// + /// Free a ptr + /// + /// + internal static void Free(IntPtr ptr) + { + if (ptr != IntPtr.Zero) + try + { + Marshal.FreeHGlobal(ptr); + } + catch (Exception ex) + { + throw new InvalidProgramException("unable to free ptr", ex); + } + } + + /// + /// Call python sandbox execute + /// + /// Python code + /// Enable network for python + /// environment + /// cache directory + /// + public static RustStdOutputManaged EvalPythonCode(string code, bool enableNetwork, Dictionary env, string cacheDir = null) + { + var sendTo = JObject.FromObject(new RustStdInputManaged + { + Code = code, + Network = enableNetwork, + Envs = env, + CachePath = cacheDir ?? "code-interpreter" + }).ToString(); + + IntPtr sendToPtr = Marshal.StringToHGlobalAnsi(sendTo); + IntPtr result = IntPtr.Zero; + try + { + result = EvalPythonCodeResultJson(sendToPtr); + var json = JObject.Parse(Marshal.PtrToStringAnsi(result)); + return json.ToObject(); + } + finally + { + Free(sendToPtr); + } + } + } +} diff --git a/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdInputManaged.cs b/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdInputManaged.cs new file mode 100644 index 00000000..019caddf --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdInputManaged.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace Pleisto.Flappy.CodeInterpreter +{ + /// + /// CodeInterpreter Input Argument + /// + internal sealed class RustStdInputManaged + { + /// + /// Python Code + /// + [JsonProperty("code")] + public string Code { get; set; } + + /// + /// Allow network + /// + [JsonProperty("network")] + public bool Network { get; set; } + + /// + /// Environments + /// + [JsonProperty("envs")] + public Dictionary Envs { get; set; } = new Dictionary(); + + /// + /// Cache of WASI + /// + [JsonProperty("cache_path")] + public string CachePath { get; set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdOutputManaged.cs b/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdOutputManaged.cs new file mode 100644 index 00000000..5f605bab --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/CodeInterpreter/RustStdOutputManaged.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace Pleisto.Flappy.CodeInterpreter +{ + /// + /// Managed of Rust Result + /// + public sealed class RustStdOutputManaged + { + /// + /// Std Error + /// + [JsonProperty("std_err", Required = Required.AllowNull)] + public string StdErr { get; private set; } + + /// + /// Std Out + /// + + [JsonProperty("std_out", Required = Required.AllowNull)] + public string StdOut { get; private set; } + + /// + /// Exception Message + /// + [JsonProperty("exception_string", Required = Required.AllowNull)] + public string ExceptionString { get; private set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/CodeInterpreterNotEnabled.cs b/packages/csharp/Pleisto.Flappy/Exceptions/CodeInterpreterNotEnabled.cs new file mode 100644 index 00000000..e448e78c --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/CodeInterpreterNotEnabled.cs @@ -0,0 +1,13 @@ +namespace Pleisto.Flappy.Exceptions +{ + /// + /// Code Interpreter not enabled exception + /// + public class CodeInterpreterNotEnabledException : Exception + { + internal CodeInterpreterNotEnabledException() : base("Code interpreter is not enabled!") + { + + } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/CodeinterpreterRetryException.cs b/packages/csharp/Pleisto.Flappy/Exceptions/CodeinterpreterRetryException.cs new file mode 100644 index 00000000..1e6fd4b2 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/CodeinterpreterRetryException.cs @@ -0,0 +1,19 @@ +namespace Pleisto.Flappy.Exceptions +{ + /// + /// Codeinterpreter too more retry exception + /// + public class CodeInterpreterRetryException : Exception + { + internal CodeInterpreterRetryException(int retryCount, Exception inner) + : base($"retried count={retryCount}", inner) + { + RetryCount = retryCount; + } + + /// + /// Retried count + /// + public int RetryCount { get; private set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonDataException.cs b/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonDataException.cs new file mode 100644 index 00000000..7cfcd3b7 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonDataException.cs @@ -0,0 +1,61 @@ +namespace Pleisto.Flappy.Exceptions +{ + + /// + /// Invalid Json data exception + /// + public class InvalidJsonDataException : Exception + { + /// + /// Unable to parse json data + /// + /// + /// + /// + /// + /// + internal InvalidJsonDataException(int startIdx, int endIdx, string raw, string splited, Exception innerException) + : base($"unable to parse json array, from:{startIdx} to:{endIdx} raw={raw} splited={splited}") + { + StartIndex = startIdx; + EndIndex = endIdx; + Raw = raw; + Splited = splited; + } + + /// + /// Unable to locate json data + /// + /// + /// + /// + /// + internal InvalidJsonDataException(int startIdx, int endIdx, string raw, Exception innerException = null) + : base($"unable to locate json data, startIndex={startIdx} endIndex={endIdx} raw={raw}", innerException) + { + StartIndex = startIdx; + EndIndex = endIdx; + Raw = raw; + } + + /// + /// Start of index + /// + public int StartIndex { get; private set; } + + /// + /// End of index + /// + public int EndIndex { get; private set; } + + /// + /// Input json string + /// + public string Raw { get; private set; } + + /// + /// Splited json string + /// + public string Splited { get; private set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonWithSchemaValidationException.cs b/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonWithSchemaValidationException.cs new file mode 100644 index 00000000..e569ae7b --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/InvalidJsonWithSchemaValidationException.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json.Schema; +using System.Text; + +namespace Pleisto.Flappy.Exceptions +{ + /// + /// Invalid json with schema validate + /// + public class InvalidJsonWithSchemaValidationException : Exception + { + internal InvalidJsonWithSchemaValidationException(IEnumerable errors) + : base($"Json Schema is invalid") + { + Errors = errors; + } + + /// + /// Json Schema Errors + /// + public IEnumerable Errors { get; } + + /// + /// A string to show the errors + /// + /// + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.AppendLine(Message); + builder.AppendLine("Errors of jsonschema:"); + foreach (var b in Errors) + builder.AppendLine(Errors.ToString()); + builder.AppendLine(); + builder.AppendLine(base.ToString()); + + return builder.ToString(); + } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/LLMNotSuccessException.cs b/packages/csharp/Pleisto.Flappy/Exceptions/LLMNotSuccessException.cs new file mode 100644 index 00000000..2e0abb28 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/LLMNotSuccessException.cs @@ -0,0 +1,13 @@ +namespace Pleisto.Flappy.Exceptions +{ + /// + /// LLM not success + /// + public class LLMNotSuccessException : Exception + { + internal LLMNotSuccessException() + : base("LLM operation return not success") + { + } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Exceptions/StepPlainException.cs b/packages/csharp/Pleisto.Flappy/Exceptions/StepPlainException.cs new file mode 100644 index 00000000..25a2e2ab --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Exceptions/StepPlainException.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json.Linq; + +namespace Pleisto.Flappy.Exceptions +{ + /// + /// Exception of plain step executing + /// + public class StepPlainException : Exception + { + internal StepPlainException(JObject config, Exception inner) + : base($"unable to process step {config}", inner) + { + } + + /// + /// Json of step + /// + public JObject StepConfigure { get; private set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/FlappyAgent.cs b/packages/csharp/Pleisto.Flappy/FlappyAgent.cs index 94800e0b..0e0168bb 100644 --- a/packages/csharp/Pleisto.Flappy/FlappyAgent.cs +++ b/packages/csharp/Pleisto.Flappy/FlappyAgent.cs @@ -1,5 +1,8 @@ +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; +using Pleisto.Flappy.CodeInterpreter; +using Pleisto.Flappy.Exceptions; using Pleisto.Flappy.Interfaces; using Pleisto.Flappy.LLM; using Pleisto.Flappy.LLM.Interfaces; @@ -16,6 +19,9 @@ public partial class FlappyAgent internal readonly FlappyAgentConfig config; internal readonly ILLMBase llm; internal readonly ILLMBase llmPlaner; + private int retry = 0; + private const int DEFAULT_RETRY = 1; + private readonly ILogger logger; /// /// Create a flappy agent. @@ -23,14 +29,17 @@ public partial class FlappyAgent /// config of flappy /// /// + /// Logger of FlappyAgent /// - public FlappyAgent(FlappyAgentConfig config, ILLMBase llm, ILLMBase llmPlaner) + public FlappyAgent(FlappyAgentConfig config, ILLMBase llm, ILLMBase llmPlaner, ILogger logger) { this.config = config; if ((config.Functions?.Length ?? 0) <= 0) throw new NullReferenceException($"config.functions not be null"); this.llm = llm ?? config.LLM; this.llmPlaner = llmPlaner ?? this.llm; + this.retry = config.Retry ?? DEFAULT_RETRY; + this.logger = logger; } /// @@ -119,12 +128,12 @@ Only the listed functions are allowed to be used." var result = await llmPlaner.ChatComplete(requestMessage, null); if (result.Success == false) { - throw new InvalidOperationException("LLM operation return not success"); + throw new LLMNotSuccessException(); } var plan = ParseComplete(result); if (plan.IsValid(zodSchema, out IList errorList) == false) - throw new InvalidDataException($"Json Schema is invalid"); + throw new InvalidJsonWithSchemaValidationException(errorList); LanOutputSchema[] plans; if (enableCot) @@ -165,7 +174,7 @@ Only the listed functions are allowed to be used." } catch (Exception ex) { - throw new InvalidProgramException($"unable to process step {Environment.NewLine}{JObject.FromObject(step)}", ex); + throw new StepPlainException(JObject.FromObject(step), ex); } return returnStore[plan.Count]; } @@ -175,7 +184,7 @@ private static JArray ParseComplete(ChatMLResponse msg) var startIdx = msg.Data?.IndexOf('[') ?? -1; var endIdx = msg.Data?.LastIndexOf(']') ?? -1; if (startIdx == -1 || endIdx == -1 || endIdx < startIdx) - throw new InvalidDataException($"Invalid JSON response startIdx={startIdx} endIdx={endIdx}"); + throw new InvalidJsonDataException(startIdx, endIdx, msg.Data); var content = msg.Data.Substring(startIdx, endIdx - startIdx + 1).Trim(); try { @@ -184,8 +193,85 @@ private static JArray ParseComplete(ChatMLResponse msg) } catch (Exception ex) { - throw new InvalidDataException($"unable to parse json array startIdx={startIdx} endIdx={endIdx} raw={Environment.NewLine}{msg.Data}" + - $"{Environment.NewLine}SplitedData:{Environment.NewLine}{content}", ex); + throw new InvalidJsonDataException(startIdx, endIdx, msg.Data, content, ex); + } + } + + /// + /// Call by code interpreter + /// + /// + /// + /// + public async Task CallCodeInterpreter(string prompt) + { + if (config.CodeInterpreter == null) + throw new CodeInterpreterNotEnabledException(); + var originalRequestMessage = new ChatMLMessage[] + { + new ChatMLMessage + { + Role = ChatMLMessageRole.System, + Content= $@"You are an AI that writes Python code using only the built-in library. After you supply the Python code, it will be run in a safe sandbox. The execution time is limited to 120 seconds. The task is to define a function named ""main"" that doesn't take any parameters. The output should be a JSON object: {{""code"": string}}. + Network access is {(config.CodeInterpreter.EnableNetwork == true ? "enabled" : "disabled")}" + }, + new ChatMLMessage + { + Role = ChatMLMessageRole.User, + Content = $"{prompt}\n\noutput json:\n" + } + }; + + var retry = this.retry; + var requestMessage = originalRequestMessage; + ChatMLResponse result = null; + while (true) + { + try + { + if (retry != this.retry) + { + logger?.LogDebug("Attempt retry: {}", this.retry - retry); + } + result = await llm.ChatComplete(requestMessage); + var data = JObject.Parse(result.Data)["code"]?.ToString()?.Replace("\\n", "\n"); + if (data == null) + throw new Exception("Invalid JSON response"); + if (data.Contains("def main():")) + throw new Exception("Function \"main\" not found"); + logger?.LogDebug("Generated Code: {}", data); + var pythonResult = NativeHandler.EvalPythonCode($"{data}\nprint(main())", config.CodeInterpreter.EnableNetwork == true, config.CodeInterpreter.Env ?? new Dictionary(), config.CodeInterpreter.CacheDir); + if (string.IsNullOrWhiteSpace(pythonResult.StdErr)) + throw new Exception(data); + logger?.LogDebug("Code Interpreter Output: {}", pythonResult.StdOut); + return pythonResult.StdOut; + } + catch (Exception ex) + { + logger?.LogError(ex.ToString()); + if (retry <= 0) + throw new CodeInterpreterRetryException(retry, ex); + retry -= 1; + if (result?.Success == true && result.Data != null) + { + requestMessage = requestMessage.Union(new ChatMLMessage[] + { + new ChatMLMessage + { + Role = ChatMLMessageRole.Assistant, + Content = result?.Data + }, + new ChatMLMessage + { + Role = ChatMLMessageRole.User, + Content= @$"You response is invalid for the following reason: + {ex.Message} + + Please try again." + } + }).ToArray(); + } + } } } diff --git a/packages/csharp/Pleisto.Flappy/Interfaces/CodeInterpreterOptions.cs b/packages/csharp/Pleisto.Flappy/Interfaces/CodeInterpreterOptions.cs new file mode 100644 index 00000000..082d36f5 --- /dev/null +++ b/packages/csharp/Pleisto.Flappy/Interfaces/CodeInterpreterOptions.cs @@ -0,0 +1,23 @@ +namespace Pleisto.Flappy.Interfaces +{ + /// + /// Core Interpreter Options + /// + public class CodeInterpreterOptions + { + /// + /// Enable network access in sandbox. Default is false. + /// + public bool? EnableNetwork { get; set; } + + /// + /// Specify the cache directory for code interpreter. + /// + public string CacheDir { get; set; } + + /// + /// Environment variables for code interpreter. + /// + public Dictionary Env { get; set; } + } +} diff --git a/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfig.cs b/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfig.cs index 436220b4..f42363bf 100644 --- a/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfig.cs +++ b/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfig.cs @@ -22,7 +22,7 @@ public class FlappyAgentConfig /// Code interpreter could be used to execute code snippets in sandbox which are generated by language model. /// If not specified, this feature will be disabled. /// - public FlappyAgentConfigCodeInterpreter? CodeInterpreter { get; set; } + public CodeInterpreterOptions CodeInterpreter { get; set; } = null; /// /// Maximum number of retries when language model generation failed. diff --git a/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfigCodeInterpreter.cs b/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfigCodeInterpreter.cs deleted file mode 100644 index fcfb75c8..00000000 --- a/packages/csharp/Pleisto.Flappy/Interfaces/FlappyAgentConfigCodeInterpreter.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Pleisto.Flappy.Interfaces -{ - /// - /// Code Interpreter Useage - /// - public enum FlappyAgentConfigCodeInterpreter - { - /// - /// Python - /// - Python, - /// - /// TypeScript - /// - [Obsolete("Not support")] - TypeScriptt - } -} diff --git a/packages/csharp/Pleisto.Flappy/Interfaces/InvokeFunctionDefinitionBase.cs b/packages/csharp/Pleisto.Flappy/Interfaces/InvokeFunctionDefinitionBase.cs deleted file mode 100644 index 957d0705..00000000 --- a/packages/csharp/Pleisto.Flappy/Interfaces/InvokeFunctionDefinitionBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; - -namespace Pleisto.Flappy.Interfaces -{ - - /// - /// Basic Definition of Invoke Function - /// - /// - /// - public class InvokeFunctionDefinitionBase - where TArgs : new() - where TReturn : new() - { - /// - /// Name - /// - [JsonRequired] - public string Name { get; set; } - - /// - /// Description - /// - public string Description { get; set; } - - //[JsonRequired] - //public TArgs args { get; set; } - - //[JsonRequired] - //public TReturn returnType { get; set; } - } -} diff --git a/packages/csharp/Pleisto.Flappy/LLM/ChatGPT.cs b/packages/csharp/Pleisto.Flappy/LLM/ChatGPT.cs index 407272e4..6d068c15 100644 --- a/packages/csharp/Pleisto.Flappy/LLM/ChatGPT.cs +++ b/packages/csharp/Pleisto.Flappy/LLM/ChatGPT.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Logging; using OpenAI_API; using OpenAI_API.Chat; using Pleisto.Flappy.LLM.Interfaces; @@ -43,20 +44,18 @@ private static int CalcDefaultMaxTokens(string model) /// Max Tokens null by AutoDetect /// /// - public ChatGPT(OpenAIAPI client, string model, int? maxTokens) + /// Logger of ChatGPT + public ChatGPT(OpenAIAPI client, string model, int? maxTokens, ILogger logger = null) { this.client = client; this.model = model; this.MaxTokens = maxTokens ?? CalcDefaultMaxTokens(model); + this.logger = logger; } private readonly string model; private readonly OpenAIAPI client; - - /// - /// Enable to show gpt debug info - /// - public bool DebugGPT { get; set; } = false; + private readonly ILogger logger; /// /// Run ChatGPT Complete @@ -66,13 +65,10 @@ public ChatGPT(OpenAIAPI client, string model, int? maxTokens) /// public virtual async Task ChatComplete(ChatMLMessage[] message, GenerateConfig config) { - if (DebugGPT) - foreach (var i in message) - { - Console.WriteLine($"=========== Role:{i.Role} ============"); - Console.WriteLine(i.Content); - Console.WriteLine($"===============END===================="); - } + foreach (var i in message) + { + logger.LogDebug("Role: {} Content: {}", i.Role, i.Content); + } var resp = await client.Chat.CreateChatCompletionAsync(new ChatRequest { Model = model, @@ -87,8 +83,7 @@ public virtual async Task ChatComplete(ChatMLMessage[] message, TopP = config?.Top_P, }); - if (DebugGPT) - Console.WriteLine($"chatGpt:{resp.Choices[0].Message.Content}"); + logger?.LogDebug("chatGpt: {}", resp.Choices[0].Message.Content); if (resp?.Choices?.Any() == true) { return new ChatMLResponse diff --git a/packages/csharp/Pleisto.Flappy/Pleisto.Flappy.csproj b/packages/csharp/Pleisto.Flappy/Pleisto.Flappy.csproj index f0aac48b..010fe148 100644 --- a/packages/csharp/Pleisto.Flappy/Pleisto.Flappy.csproj +++ b/packages/csharp/Pleisto.Flappy/Pleisto.Flappy.csproj @@ -3,7 +3,7 @@ Pleisto Pleisto.Flappy - net7.0;net6.0;netcoreapp3.1;netstandard2.0;netstandard2.1 + net7.0;net6.0;netcoreapp3.1;netstandard2.1;net48;net472 enable 11.0 latest @@ -11,23 +11,26 @@ disable true IDE1006 + true LICENSE README.md https://github.com/pleisto/flappy true true - 0.0.0.3-alpha + 0.0.0.4-alpha agent;transfromers;llama;llm;generative-ai;chatgpt;rewoo + - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -37,6 +40,10 @@ + + True + runtimes + diff --git a/packages/csharp/Pleisto.Flappy/SynthesizedFunction.cs b/packages/csharp/Pleisto.Flappy/SynthesizedFunction.cs index 63ff9e6a..e1b40bdd 100644 --- a/packages/csharp/Pleisto.Flappy/SynthesizedFunction.cs +++ b/packages/csharp/Pleisto.Flappy/SynthesizedFunction.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json.Linq; +using Pleisto.Flappy.Exceptions; using Pleisto.Flappy.Interfaces; using Pleisto.Flappy.LLM.Interfaces; @@ -73,7 +74,7 @@ private TReturn ParseComplete(ChatMLResponse msg) var startIdx = msg.Data?.IndexOf('{') ?? -1; var endIdx = msg.Data?.LastIndexOf('}') ?? -1; if (startIdx == -1 || endIdx == -1 || endIdx < startIdx) - throw new InvalidDataException($"Invalid JSON response startIndex={startIdx} endIdx={endIdx}"); + throw new InvalidJsonDataException(startIdx, endIdx, msg.Data); var content = msg.Data.Substring(startIdx, endIdx - startIdx + 1).Trim(); try @@ -83,7 +84,7 @@ private TReturn ParseComplete(ChatMLResponse msg) } catch (Exception ex) { - throw new InvalidDataException($"exception on {nameof(ParseComplete)} {Environment.NewLine}{content}", ex); + throw new InvalidJsonDataException(startIdx, endIdx, msg.Data, content, ex); } } diff --git a/packages/rust-core/Cargo.lock b/packages/rust-core/Cargo.lock index 176f6ad7..c49615d8 100644 --- a/packages/rust-core/Cargo.lock +++ b/packages/rust-core/Cargo.lock @@ -859,6 +859,19 @@ dependencies = [ "webc", ] +[[package]] +name = "flappy-csharp-bindings" +version = "0.1.0" +dependencies = [ + "flappy-common", + "libc", + "mimalloc", + "openssl", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "flappy-java-bindings" version = "0.1.0" @@ -1859,6 +1872,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + [[package]] name = "parking_lot_core" version = "0.9.8" @@ -2570,6 +2593,15 @@ dependencies = [ "memmap2 0.6.2", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -2809,7 +2841,9 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", diff --git a/packages/rust-core/Cargo.toml b/packages/rust-core/Cargo.toml index 63cab3a4..e4791f2e 100644 --- a/packages/rust-core/Cargo.toml +++ b/packages/rust-core/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["./common", "./nodejs", "./java"] +members = ["./common", "./nodejs","./java", "./csharp"] resolver = "2" package.license = "Apache-2.0" diff --git a/packages/rust-core/build.sh b/packages/rust-core/build.sh new file mode 100644 index 00000000..58a9025e --- /dev/null +++ b/packages/rust-core/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash +build(){ + local toolchain=$2 + local target=$1 + + rustup target add $target + if [ ! "$toolchain" == "" ]; then + rustup toolchain add $toolchain + fi + + cargo build --target=$target -r -j 48 +} + +set -e + +if [ "$1" == "" ]; then + + build aarch64-unknown-linux-gnu + + + build x86_64-unknown-linux-gnu + + build aarch64-apple-darwin + + build x86_64-pc-windows-msvc +fi \ No newline at end of file diff --git a/packages/rust-core/csharp/.eslintrc.yml b/packages/rust-core/csharp/.eslintrc.yml new file mode 100644 index 00000000..4e690c60 --- /dev/null +++ b/packages/rust-core/csharp/.eslintrc.yml @@ -0,0 +1,4 @@ +root: true + +extends: + - plugin:@pleisto/eslint-plugin/base diff --git a/packages/rust-core/csharp/.gitignore b/packages/rust-core/csharp/.gitignore new file mode 100644 index 00000000..265b8625 --- /dev/null +++ b/packages/rust-core/csharp/.gitignore @@ -0,0 +1,420 @@ +index.node +# ---> VisualStudio +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +# ---> VisualStudioCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +dist +dist-multifile +dist-normal + +app/ \ No newline at end of file diff --git a/packages/rust-core/csharp/Cargo.toml b/packages/rust-core/csharp/Cargo.toml new file mode 100644 index 00000000..cbce6147 --- /dev/null +++ b/packages/rust-core/csharp/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "flappy-csharp-bindings" +version = "0.1.0" +edition = "2021" + +[package.metadata.workspaces] +independent = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +flappy-common = { path = "../common" } +mimalloc = {version = "0.1.39", default-features = false} +libc = "0.2" +tokio = { version = "1.0", features = ["full"] } +openssl = { version = "0.10", features = ["vendored"] } +serde = "1.0" +serde_json = "1.0" + +[profile.release] +lto = true +codegen-units = 1 + +[profile.dev] +incremental=true diff --git a/packages/rust-core/csharp/src/code_interpreter/mod.rs b/packages/rust-core/csharp/src/code_interpreter/mod.rs new file mode 100644 index 00000000..3aa56462 --- /dev/null +++ b/packages/rust-core/csharp/src/code_interpreter/mod.rs @@ -0,0 +1,83 @@ +use flappy_common::code_interpreter::wasix::*; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +use tokio::runtime::Builder; + +fn str2ptr(str: String) -> *const c_char { + CString::new(str) + .expect("Failed to convert String to CString") + .into_raw() +} + +fn ptr2str(c_char_ptr: *const std::os::raw::c_char) -> String { + unsafe { + if c_char_ptr.is_null() { + return String::new(); + } + + let c_str = CStr::from_ptr(c_char_ptr); + let string_result = c_str.to_str(); + + match string_result { + Ok(string) => string.to_owned(), + Err(_) => String::new(), + } + } +} + +/// Just of Native Call Test +#[no_mangle] +pub extern "C" fn eval_native_call() -> bool { + true +} + +#[derive(Debug, Serialize, Deserialize)] +struct InputStruct { + code: String, + network: bool, + envs: HashMap, + cache_path: String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct OutputStruct { + std_out: String, + std_err: String, + exception_string: String, +} + +/// Call python code by json in json out +#[no_mangle] +pub extern "C" fn eval_python_code_by_json(input_str: *const c_char) -> *const c_char { + let input_string = ptr2str(input_str); + let input: InputStruct = serde_json::from_str(&input_string).unwrap(); + + let rt = Builder::new_multi_thread().enable_all().build().unwrap(); + let output = rt.block_on(async { + match python_sandbox( + input.code, + input.network, + input.envs.into_iter().collect(), + Some(input.cache_path), + ) + .await + { + Err(err) => OutputStruct { + exception_string: err.to_string(), + std_err: String::new(), + std_out: String::new(), + }, + Ok(output) => OutputStruct { + std_err: output.stderr, + std_out: output.stdout, + exception_string: String::new(), + }, + } + }); + + str2ptr(serde_json::to_string(&output).expect("Failed to serialize struct to JSON")) +} diff --git a/packages/rust-core/csharp/src/lib.rs b/packages/rust-core/csharp/src/lib.rs new file mode 100644 index 00000000..73e168c0 --- /dev/null +++ b/packages/rust-core/csharp/src/lib.rs @@ -0,0 +1,8 @@ +use mimalloc::MiMalloc; + +/// mimalloc is a compact general purpose allocator with excellent performance. +/// https://github.com/microsoft/mimalloc +#[global_allocator] +static GLOBAL: MiMalloc = MiMalloc; + +pub mod code_interpreter; diff --git a/packages/rust-core/csharp/tsconfig.json b/packages/rust-core/csharp/tsconfig.json new file mode 100644 index 00000000..449c3f2b --- /dev/null +++ b/packages/rust-core/csharp/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tools/typescript/tsconfig.cjs.json" +}