Skip to content

Commit 05f1dc6

Browse files
committed
add endpoints
1 parent 6783039 commit 05f1dc6

6 files changed

+117
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Azure;
2+
using Azure.AI.OpenAI;
3+
using DevExpress.Drawing;
4+
using DevExpress.Office.Utils;
5+
6+
namespace RichEditOpenAIWebApi.BusinessObjects
7+
{
8+
public class OpenAIClientHyperlinkHelper
9+
{
10+
OpenAIClient client;
11+
internal OpenAIClientHyperlinkHelper(string openAIApiKey)
12+
{
13+
client = new OpenAIClient(openAIApiKey, new OpenAIClientOptions());
14+
}
15+
internal async Task<string> DescribeHyperlinkAsync(string link)
16+
{
17+
ChatCompletionsOptions chatCompletionsOptions = new()
18+
{
19+
DeploymentName = "gpt-4-vision-preview",
20+
Messages =
21+
{
22+
new ChatRequestSystemMessage("You are a helpful assistant that describes images."),
23+
new ChatRequestUserMessage(
24+
new ChatMessageTextContentItem("Give a description of this hyperlink URI in 10-20 words"),
25+
new ChatMessageTextContentItem(link))
26+
},
27+
MaxTokens = 300
28+
};
29+
30+
Response<ChatCompletions> chatResponse = await client.GetChatCompletionsAsync(chatCompletionsOptions);
31+
ChatChoice choice = chatResponse.Value.Choices[0];
32+
return choice.Message.Content;
33+
}
34+
}
35+
}

CS/BusinessObjects/OpenAIClientImageHelper.cs

+4-7
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,17 @@ string ConvertDXImageToBase64String(DXImage image) {
1717
}
1818
}
1919
internal async Task<string> DescribeImageAsync(OfficeImage image) {
20-
string base64Content = ConvertDXImageToBase64String(image.DXImage);
21-
string imageContentType = OfficeImage.GetContentType(OfficeImageFormat.Png);
22-
return await GetImageDescription($"data:{imageContentType};base64,{base64Content}");
20+
return await GetImageDescription(image.GetImageBytes(OfficeImageFormat.Png));
2321
}
24-
internal async Task<string> GetImageDescription(string uriString) {
22+
internal async Task<string> GetImageDescription(byte[] imageBytes) {
2523
ChatCompletionsOptions chatCompletionsOptions = new() {
2624
DeploymentName = "gpt-4-vision-preview",
2725
Messages =
2826
{
29-
new ChatRequestSystemMessage("You are a helpful assistant that describes images."),
27+
new ChatRequestSystemMessage("You are a helpful assistant that describes hyperlinks."),
3028
new ChatRequestUserMessage(
3129
new ChatMessageTextContentItem("Give a description of this image in no more than 10 words"),
32-
new ChatMessageImageContentItem(new Uri(uriString))),
30+
new ChatMessageImageContentItem(BinaryData.FromBytes(imageBytes), "image/png"))
3331
},
3432
MaxTokens = 300
3533
};
@@ -38,6 +36,5 @@ internal async Task<string> GetImageDescription(string uriString) {
3836
ChatChoice choice = chatResponse.Value.Choices[0];
3937
return choice.Message.Content;
4038
}
41-
4239
}
4340
}

CS/BusinessObjects/RichEditExtension.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace RichEditOpenAIWebApi.BusinessObjects {
55
static class RichEditExtension {
6-
internal static void GenerateAltTextForImages(this IRichEditDocumentServer server, Action<SubDocument> action) {
6+
internal static void IterateSubDocuments(this IRichEditDocumentServer server, Action<SubDocument> action) {
77
Document document = server.Document;
88
UpdateSubDocument(document, action);
99
foreach (var section in document.Sections) {

CS/Controllers/OpenAIController.cs

+71-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using DevExpress.Office.Utils;
22
using DevExpress.Spreadsheet;
3+
using DevExpress.XtraPrinting.Native;
34
using DevExpress.XtraRichEdit;
5+
using DevExpress.XtraRichEdit.API.Native;
46
using Microsoft.AspNetCore.Mvc;
57
using RichEditOpenAIWebApi.BusinessObjects;
68
using Swashbuckle.AspNetCore.Annotations;
9+
using System.Globalization;
710
using System.Net;
811

912
namespace RichEditOpenAIWebApi.Controllers
@@ -12,6 +15,7 @@ namespace RichEditOpenAIWebApi.Controllers
1215
[Route("[controller]/[action]")]
1316
public class OpenAIController : ControllerBase
1417
{
18+
// Insert your OpenAI key
1519
string openAIApiKey = "";
1620
[HttpPost]
1721
[SwaggerResponse((int)HttpStatusCode.OK, "Download a file", typeof(FileContentResult))]
@@ -20,11 +24,11 @@ public async Task<IActionResult> GenerateImageAltText(IFormFile documentWithImag
2024
try
2125
{
2226
var imageHelper = new OpenAIClientImageHelper(openAIApiKey);
23-
using (var server = new RichEditDocumentServer())
27+
using (var wordProcessor = new RichEditDocumentServer())
2428
{
25-
await RichEditHelper.LoadFile(server, documentWithImage);
29+
await RichEditHelper.LoadFile(wordProcessor, documentWithImage);
2630

27-
server.GenerateAltTextForImages((document) =>
31+
wordProcessor.IterateSubDocuments((document) =>
2832
{
2933
foreach (var shape in document.Shapes)
3034
{
@@ -33,7 +37,7 @@ public async Task<IActionResult> GenerateImageAltText(IFormFile documentWithImag
3337
}
3438
});
3539

36-
Stream result = RichEditHelper.SaveDocument(server, outputFormat);
40+
Stream result = RichEditHelper.SaveDocument(wordProcessor, outputFormat);
3741
string contentType = RichEditHelper.GetContentType(outputFormat);
3842
string outputStringFormat = outputFormat.ToString().ToLower();
3943
return File(result, contentType, $"result.{outputStringFormat}");
@@ -61,7 +65,7 @@ public async Task<IActionResult> GenerateChartAltText(IFormFile documentWithImag
6165
foreach (var chart in worksheet.Charts)
6266
{
6367
OfficeImage image = chart.ExportToImage();
64-
chart.AlternativeText = await imageHelper.DescribeImageAsync(image);
68+
chart.AlternativeText = imageHelper.DescribeImageAsync(image).Result;
6569
}
6670
}
6771

@@ -75,7 +79,69 @@ public async Task<IActionResult> GenerateChartAltText(IFormFile documentWithImag
7579
{
7680
return StatusCode(500, e.Message + Environment.NewLine + e.StackTrace);
7781
}
82+
}
83+
[HttpPost]
84+
[SwaggerResponse((int)HttpStatusCode.OK, "Download a file", typeof(FileContentResult))]
85+
public async Task<IActionResult> GenerateHyperlinkDescriptionForWord(IFormFile documentWithHyperlinks, [FromQuery] RichEditFormat outputFormat)
86+
{
87+
try
88+
{
89+
var hyperlinkHelper = new OpenAIClientHyperlinkHelper(openAIApiKey);
90+
using (var wordProcessor = new RichEditDocumentServer())
91+
{
92+
await RichEditHelper.LoadFile(wordProcessor, documentWithHyperlinks);
7893

94+
wordProcessor.IterateSubDocuments(async (document) =>
95+
{
96+
foreach (var hyperlink in document.Hyperlinks)
97+
{
98+
if (string.IsNullOrEmpty(hyperlink.ToolTip) || hyperlink.ToolTip == hyperlink.NavigateUri)
99+
{
100+
hyperlink.ToolTip = hyperlinkHelper.DescribeHyperlinkAsync(hyperlink.NavigateUri).Result;
101+
}
102+
}
103+
});
104+
Stream result = RichEditHelper.SaveDocument(wordProcessor, outputFormat);
105+
string contentType = RichEditHelper.GetContentType(outputFormat);
106+
string outputStringFormat = outputFormat.ToString().ToLower();
107+
return File(result, contentType, $"result.{outputStringFormat}");
108+
}
109+
}
110+
catch (Exception e)
111+
{
112+
return StatusCode(500, e.Message + Environment.NewLine + e.StackTrace);
113+
}
79114
}
115+
[HttpPost]
116+
[SwaggerResponse((int)HttpStatusCode.OK, "Download a file", typeof(FileContentResult))]
117+
public async Task<IActionResult> GenerateHyperlinkDescriptionForSpreadsheet(IFormFile documentWithHyperlinks, [FromQuery] SpreadsheetFormat outputFormat)
118+
{
119+
try
120+
{
121+
var hyperlinkHelper = new OpenAIClientHyperlinkHelper(openAIApiKey);
122+
using (var workbook = new Workbook())
123+
{
124+
await SpreadsheetHelper.LoadWorkbook(workbook, documentWithHyperlinks);
125+
126+
foreach (var worksheet in workbook.Worksheets)
127+
{
128+
foreach (var hyperlink in worksheet.Hyperlinks)
129+
{
130+
if(hyperlink.IsExternal && (string.IsNullOrEmpty(hyperlink.TooltipText) || hyperlink.TooltipText == hyperlink.Uri))
131+
hyperlink.TooltipText = hyperlinkHelper.DescribeHyperlinkAsync(hyperlink.Uri).Result;
132+
}
133+
}
134+
135+
Stream result = SpreadsheetHelper.SaveDocument(workbook, outputFormat);
136+
string contentType = SpreadsheetHelper.GetContentType(outputFormat);
137+
string outputStringFormat = outputFormat.ToString().ToLower();
138+
return File(result, contentType, $"result.{outputStringFormat}");
139+
}
140+
}
141+
catch (Exception e)
142+
{
143+
return StatusCode(500, e.Message + Environment.NewLine + e.StackTrace);
144+
}
145+
}
80146
}
81147
}

CS/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build runtime image
2-
FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim AS base
2+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
33
WORKDIR /app
44
EXPOSE 80
55
EXPOSE 443
@@ -14,7 +14,7 @@ RUN echo "deb http://ftp.debian.org/debian/ bullseye contrib" >> /etc/apt/source
1414
RUN apt update
1515
RUN apt install -y ttf-mscorefonts-installer
1616

17-
FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim AS build
17+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
1818
WORKDIR /src
1919

2020
# Specify your DevExpress NuGet Feed URL as the package source

CS/RichEditOpenAIWebApi.csproj

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<UserSecretsId>c8ad3c81-f33a-4c4a-b63f-5d8d4c6a43b8</UserSecretsId>
@@ -10,10 +10,11 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.12" />
13+
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
14+
<PackageReference Include="Azure.AI.TextAnalytics" Version="5.3.0" />
1415
<PackageReference Include="DevExpress.Document.Processor" Version="23.2.5" />
1516
<PackageReference Include="DevExpress.Drawing.Skia" Version="23.2.5" />
16-
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.11" />
17+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
1718
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
1819
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
1920
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />

0 commit comments

Comments
 (0)