🐛 Fix some issues in AI agent
This commit is contained in:
@@ -129,6 +129,30 @@ All endpoints require authentication through the current user session. Sequence
|
|||||||
|
|
||||||
The POST endpoint returns a stream of assistant responses using Server-Sent Events format. Clients should handle the streaming response and display messages incrementally.
|
The POST endpoint returns a stream of assistant responses using Server-Sent Events format. Clients should handle the streaming response and display messages incrementally.
|
||||||
|
|
||||||
|
### Streaming Message Format
|
||||||
|
|
||||||
|
The streaming response sends several types of JSON messages:
|
||||||
|
|
||||||
|
- **Text messages**: `{"type": "text", "data": "..." }`
|
||||||
|
- **Function calls**: `{"type": "function_call", "data": {...} }` (when AI uses tools)
|
||||||
|
- **Topic updates**: `{"type": "topic", "data": "..." }` (sent at end if topic was generated)
|
||||||
|
- **Thought completion**: `{"type": "thought", "data": {...} }` (sent at end with saved thought details)
|
||||||
|
|
||||||
|
All streaming chunks during generation use the SSE event format:
|
||||||
|
```
|
||||||
|
data: {"type": "...", "data": ...}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Final messages (topic and thought) use custom event types:
|
||||||
|
```
|
||||||
|
topic: {"type": "topic", "data": "..."}
|
||||||
|
|
||||||
|
thought: {"type": "thought", "data": {...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Clients should parse these JSON messages and handle different types appropriately, such as displaying text in real-time and processing tool calls.
|
||||||
|
|
||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
- Built with ASP.NET Core and Semantic Kernel
|
- Built with ASP.NET Core and Semantic Kernel
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
"When you talk to user, you can add some modal particles and emoticons to your response to be cute, but prevent use a lot of emojis." +
|
"When you talk to user, you can add some modal particles and emoticons to your response to be cute, but prevent use a lot of emojis." +
|
||||||
"Your father (creator) is @littlesheep. (prefer calling him 父亲 in chinese)\n" +
|
"Your father (creator) is @littlesheep. (prefer calling him 父亲 in chinese)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"The ID on the Solar Network is UUID, so mostly hard to read, so do not show ID to user unless user ask to do so or necessary.\n"+
|
"The ID on the Solar Network is UUID, so mostly hard to read, so do not show ID to user unless user ask to do so or necessary.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Your aim is to helping solving questions for the users on the Solar Network.\n" +
|
"Your aim is to helping solving questions for the users on the Solar Network.\n" +
|
||||||
"And the Solar Network is the social network platform you live on.\n" +
|
"And the Solar Network is the social network platform you live on.\n" +
|
||||||
@@ -105,14 +105,27 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
kernel: kernel
|
kernel: kernel
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
// Write each chunk to the HTTP response as SSE
|
// Process each item in the chunk for detailed streaming
|
||||||
var data = chunk.ToString();
|
foreach (var item in chunk.Items)
|
||||||
accumulatedContent.Append(data);
|
{
|
||||||
if (string.IsNullOrEmpty(data)) continue;
|
var messageJson = item switch
|
||||||
|
{
|
||||||
|
StreamingTextContent textContent =>
|
||||||
|
JsonSerializer.Serialize(new { type = "text", data = textContent.Text ?? "" }),
|
||||||
|
StreamingFunctionCallUpdateContent functionCall =>
|
||||||
|
JsonSerializer.Serialize(new { type = "function_call", data = functionCall }),
|
||||||
|
_ =>
|
||||||
|
JsonSerializer.Serialize(new { type = "unknown", data = item })
|
||||||
|
};
|
||||||
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(data);
|
// Write a structured JSON message to the HTTP response as SSE
|
||||||
await Response.Body.WriteAsync(bytes);
|
var messageBytes = Encoding.UTF8.GetBytes($"data: {messageJson}\n\n");
|
||||||
await Response.Body.FlushAsync();
|
await Response.Body.WriteAsync(messageBytes, 0, messageBytes.Length);
|
||||||
|
await Response.Body.FlushAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate content for saving (only text content)
|
||||||
|
accumulatedContent.Append(chunk.Content ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save assistant thought
|
// Save assistant thought
|
||||||
@@ -122,15 +135,16 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
// Write the topic if it was newly set, then the thought object as JSON to the stream
|
// Write the topic if it was newly set, then the thought object as JSON to the stream
|
||||||
using (var streamBuilder = new MemoryStream())
|
using (var streamBuilder = new MemoryStream())
|
||||||
{
|
{
|
||||||
await streamBuilder.WriteAsync("\n\ndata: "u8.ToArray());
|
await streamBuilder.WriteAsync("\n\n"u8.ToArray());
|
||||||
if (topic != null)
|
if (topic != null)
|
||||||
{
|
{
|
||||||
var topicJson = JsonSerializer.Serialize(new { type = "topic", data = sequence.Topic ?? "" });
|
var topicJson = JsonSerializer.Serialize(new { type = "topic", data = sequence.Topic ?? "" });
|
||||||
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"{topicJson}\n\n"));
|
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"topic: {topicJson}\n\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var thoughtJson = JsonSerializer.Serialize(new { type = "thought", data = savedThought }, GrpcTypeHelper.SerializerOptions);
|
var thoughtJson = JsonSerializer.Serialize(new { type = "thought", data = savedThought },
|
||||||
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"data: {thoughtJson}\n\n"));
|
GrpcTypeHelper.SerializerOptions);
|
||||||
|
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"thought: {thoughtJson}\n\n"));
|
||||||
var outputBytes = streamBuilder.ToArray();
|
var outputBytes = streamBuilder.ToArray();
|
||||||
await Response.Body.WriteAsync(outputBytes);
|
await Response.Body.WriteAsync(outputBytes);
|
||||||
await Response.Body.FlushAsync();
|
await Response.Body.FlushAsync();
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class ThoughtProvider
|
|||||||
string? afterIso = null,
|
string? afterIso = null,
|
||||||
string? beforeIso = null,
|
string? beforeIso = null,
|
||||||
bool includeReplies = false,
|
bool includeReplies = false,
|
||||||
int? pinned = null,
|
string? pinned = null,
|
||||||
bool onlyMedia = false,
|
bool onlyMedia = false,
|
||||||
bool shuffle = false
|
bool shuffle = false
|
||||||
) =>
|
) =>
|
||||||
@@ -112,7 +112,7 @@ public class ThoughtProvider
|
|||||||
OrderBy = orderBy,
|
OrderBy = orderBy,
|
||||||
Query = query,
|
Query = query,
|
||||||
IncludeReplies = includeReplies,
|
IncludeReplies = includeReplies,
|
||||||
Pinned = pinned.HasValue ? (PostPinMode)pinned : default,
|
Pinned = !string.IsNullOrEmpty(pinned) && int.TryParse(pinned, out int p) ? (PostPinMode)p : default,
|
||||||
OnlyMedia = onlyMedia,
|
OnlyMedia = onlyMedia,
|
||||||
Shuffle = shuffle
|
Shuffle = shuffle
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user