648 lines
28 KiB
HTML
648 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fa" dir="rtl">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>پیادهسازی استخراج محتوا با استفاده از xFileService در xAiApi - فن آوران ساحر علم</title>
|
||
<style>
|
||
:root {
|
||
--primary: #1e3a8a;
|
||
--secondary: #3b82f6;
|
||
--accent: #f59e0b;
|
||
--success: #10b981;
|
||
--danger: #ef4444;
|
||
--warning: #f97316;
|
||
--bg-light: #f8fafc;
|
||
--bg-code: #1e293b;
|
||
--text-dark: #0f172a;
|
||
--text-muted: #64748b;
|
||
--border: #e2e8f0;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: 'Tahoma', 'Segoe UI', sans-serif;
|
||
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
|
||
color: var(--text-dark);
|
||
line-height: 1.8;
|
||
padding: 20px;
|
||
}
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
border-radius: 16px;
|
||
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
|
||
overflow: hidden;
|
||
}
|
||
.header {
|
||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||
color: white;
|
||
padding: 40px;
|
||
text-align: center;
|
||
position: relative;
|
||
}
|
||
.header h1 { font-size: 2.2em; margin-bottom: 10px; position: relative; }
|
||
.header .subtitle { font-size: 1.1em; opacity: 0.95; position: relative; }
|
||
.meta-bar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
background: var(--bg-light);
|
||
padding: 15px 30px;
|
||
border-bottom: 2px solid var(--border);
|
||
flex-wrap: wrap;
|
||
gap: 15px;
|
||
}
|
||
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
|
||
.meta-item strong { color: var(--primary); }
|
||
.content { padding: 40px; }
|
||
.section {
|
||
margin-bottom: 35px;
|
||
padding: 25px;
|
||
background: var(--bg-light);
|
||
border-radius: 12px;
|
||
border-right: 5px solid var(--secondary);
|
||
}
|
||
.section h2 {
|
||
color: var(--primary);
|
||
font-size: 1.6em;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid var(--border);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
|
||
.step {
|
||
background: white;
|
||
padding: 20px;
|
||
margin: 15px 0;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
border-right: 4px solid var(--accent);
|
||
}
|
||
.step-number {
|
||
display: inline-block;
|
||
background: var(--accent);
|
||
color: white;
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
text-align: center;
|
||
line-height: 32px;
|
||
font-weight: bold;
|
||
margin-left: 10px;
|
||
}
|
||
.arch-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
.arch-card {
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||
border-top: 4px solid var(--secondary);
|
||
transition: transform 0.3s;
|
||
}
|
||
.arch-card:hover { transform: translateY(-5px); }
|
||
.arch-card h4 { color: var(--primary); margin-bottom: 12px; font-size: 1.15em; }
|
||
.arch-card ul { list-style: none; padding-right: 0; }
|
||
.arch-card li { padding: 6px 0; padding-right: 20px; position: relative; font-size: 0.95em; }
|
||
.arch-card li::before { content: '▸'; position: absolute; right: 0; color: var(--accent); font-weight: bold; }
|
||
.tech-badge {
|
||
display: inline-block;
|
||
background: var(--secondary);
|
||
color: white;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 0.85em;
|
||
margin: 3px;
|
||
}
|
||
.tech-badge.primary { background: var(--primary); }
|
||
.tech-badge.success { background: var(--success); }
|
||
.tech-badge.accent { background: var(--accent); }
|
||
pre {
|
||
background: var(--bg-code);
|
||
color: #e2e8f0;
|
||
padding: 18px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
direction: ltr;
|
||
text-align: left;
|
||
font-family: 'Consolas', 'Courier New', monospace;
|
||
font-size: 0.85em;
|
||
margin: 15px 0;
|
||
border-right: 4px solid var(--accent);
|
||
}
|
||
code {
|
||
background: #fef3c7;
|
||
color: #92400e;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-family: 'Consolas', monospace;
|
||
font-size: 0.9em;
|
||
direction: ltr;
|
||
display: inline-block;
|
||
}
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 15px 0;
|
||
background: white;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||
}
|
||
th { background: var(--primary); color: white; padding: 12px; text-align: right; font-weight: bold; }
|
||
td { padding: 12px; border-bottom: 1px solid var(--border); }
|
||
tr:hover { background: var(--bg-light); }
|
||
.flow-diagram {
|
||
background: white;
|
||
padding: 25px;
|
||
border-radius: 10px;
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
}
|
||
.flow-step {
|
||
display: inline-block;
|
||
background: var(--secondary);
|
||
color: white;
|
||
padding: 10px 20px;
|
||
border-radius: 8px;
|
||
margin: 5px;
|
||
font-size: 0.9em;
|
||
}
|
||
.flow-arrow { display: inline-block; color: var(--accent); font-size: 1.5em; margin: 0 8px; vertical-align: middle; }
|
||
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
|
||
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
|
||
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
|
||
.alert-warning { background: #fef3c7; border-color: var(--accent); color: #92400e; }
|
||
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; margin-top: 40px; }
|
||
.footer p { margin: 5px 0; }
|
||
.highlight { background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%); padding: 2px 6px; border-radius: 4px; font-weight: bold; }
|
||
.toc { background: white; padding: 20px; border-radius: 10px; margin-bottom: 25px; border: 2px solid var(--border); }
|
||
.toc h3 { color: var(--primary); margin-bottom: 15px; }
|
||
.toc ol { padding-right: 25px; }
|
||
.toc li { padding: 6px 0; }
|
||
.toc a { color: var(--secondary); text-decoration: none; transition: color 0.2s; }
|
||
.toc a:hover { color: var(--primary); text-decoration: underline; }
|
||
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
|
||
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
|
||
.badge-new { background: var(--success); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<div class="header">
|
||
<h1>📎 پیادهسازی استخراج محتوا با ماژول xFileService</h1>
|
||
<div class="subtitle">یکپارچهسازی XFileProvider در XAiServiceBase برای پردازش فایلهای ضمیمه</div>
|
||
</div>
|
||
|
||
<div class="meta-bar">
|
||
<div class="meta-item">👨💻 <strong>توسعهدهنده:</strong> هادی خزاعی اصل</div>
|
||
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
|
||
<div class="meta-item">📅 <strong>تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</div>
|
||
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
|
||
</div>
|
||
|
||
<div class="content">
|
||
|
||
<div class="toc">
|
||
<h3>📑 فهرست مطالب</h3>
|
||
<ol>
|
||
<li><a href="#strategy">استراتژی یکپارچهسازی</a></li>
|
||
<li><a href="#step1">گام ۱: بهروزرسانی Controller برای آپلود فایل</a></li>
|
||
<li><a href="#step2">گام ۲: تزریق IXFileProvider به XAiServiceBase</a></li>
|
||
<li><a href="#step3">گام ۳: پیادهسازی منطق استخراج محتوا از فایل</a></li>
|
||
<li><a href="#step4">گام ۴: الحاق محتوا به ChatMessage و ذخیره Metadata</a></li>
|
||
<li><a href="#step5">گام ۵: بهروزرسانی Extension مدلها</a></li>
|
||
<li><a href="#flow">جریان کامل دادهها</a></li>
|
||
<li><a href="#summary">خلاصه تغییرات</a></li>
|
||
</ol>
|
||
</div>
|
||
|
||
<!-- Section 1: Strategy -->
|
||
<div class="section" id="strategy">
|
||
<h2>🎯 گام ۱: استراتژی یکپارچهسازی</h2>
|
||
<p>
|
||
به جای مدیریت مستقیم <code>IFormFile</code> در لایه سرویس هوش مصنوعی، از معماری تمیز (Clean Architecture) پیروی میکنیم:
|
||
</p>
|
||
<div class="arch-grid">
|
||
<div class="arch-card">
|
||
<h4>۱. لایه ارائه (Controller)</h4>
|
||
<ul>
|
||
<li>دریافت <code>IFormFileCollection</code></li>
|
||
<li>فراخوانی <code>IXFileProvider.Upload</code></li>
|
||
<li>دریافت لیست <code>XFileDto</code></li>
|
||
</ul>
|
||
</div>
|
||
<div class="arch-card">
|
||
<h4>۲. لایه سرویس (XAiServiceBase)</h4>
|
||
<ul>
|
||
<li>دریافت <code>IEnumerable<XFileDto></code></li>
|
||
<li>فراخوانی <code>IXFileProvider.GetFileDescriptor</code></li>
|
||
<li>خواندن Stream و تبدیل به <code>AIContent</code></li>
|
||
</ul>
|
||
</div>
|
||
<div class="arch-card">
|
||
<h4>۳. لایه داده (Metadata)</h4>
|
||
<ul>
|
||
<li>ذخیره <code>FileId</code> در فیلد <code>MetaDatas</code> پیام</li>
|
||
<li>حفظ رابطه بدون نیاز به جدول Join جدید</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 1 -->
|
||
<div class="section" id="step1">
|
||
<h2>🎮 گام ۲: بهروزرسانی Controller برای آپلود فایل</h2>
|
||
<p>ابتدا باید فایلها را از طریق <code>XFileProvider</code> آپلود کنیم تا <code>XFileDto</code> دریافت شود.</p>
|
||
|
||
<div class="file-change">
|
||
<span class="badge-modify">MODIFY</span>
|
||
<span class="path">xAiApi/Controllers/XAiServiceControllerBase.cs</span>
|
||
</div>
|
||
|
||
<pre>// ۱. افزودن وابستگی به Constructor
|
||
private readonly IXFileProvider _fileProvider;
|
||
|
||
protected XAiServiceControllerBase(
|
||
// ... پارامترهای قبلی
|
||
IXFileProvider fileProvider // ✅ جدید
|
||
) : base(...)
|
||
{
|
||
// ...
|
||
_fileProvider = fileProvider;
|
||
}
|
||
|
||
// ۲. اصلاح متد Ask
|
||
[HttpPost("Ask")]
|
||
[Consumes("multipart/form-data")]
|
||
public async Task<ActionResult<string>> Ask(
|
||
[FromForm] XAiResponseRequest request,
|
||
[FromForm] IFormFileCollection files,
|
||
CancellationToken cancellationToken = default
|
||
)
|
||
{
|
||
try
|
||
{
|
||
if (!request.IsValid()) XException.InvalidArgs.Throw();
|
||
|
||
var userInfo = await GetUserInfo();
|
||
var connectionId = GetConnectionId();
|
||
|
||
// ✅ آپلود فایلها از طریق ماژول xFileService
|
||
IEnumerable<XFileDto> uploadedFiles = null;
|
||
if (files != null && files.Any())
|
||
{
|
||
uploadedFiles = await _fileProvider.Upload(
|
||
files: files,
|
||
userInfo: userInfo,
|
||
connectionId: connectionId,
|
||
cancellationToken: cancellationToken
|
||
);
|
||
}
|
||
|
||
// ✅ ارسال XFileDto به سرویس هوش مصنوعی
|
||
var result = await aiService.AskAsync(
|
||
prompt: request.Prompt,
|
||
ownerId: userInfo.UserId,
|
||
projectId: request.ProjectId,
|
||
conversationId: request.ConversationId,
|
||
connectionId: connectionId,
|
||
attachedFiles: uploadedFiles, // ✅ تغییر نوع پارامتر
|
||
cancellationToken: cancellationToken
|
||
);
|
||
|
||
return Ok(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return GetExceptionActionResult(ex);
|
||
}
|
||
}</pre>
|
||
<div class="alert alert-info">
|
||
<strong>💡 نکته:</strong> همین تغییر باید برای متد <code>AskStream</code> نیز اعمال شود.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2 -->
|
||
<div class="section" id="step2">
|
||
<h2>⚙️ گام ۳: تزریق IXFileProvider به XAiServiceBase</h2>
|
||
|
||
<div class="file-change">
|
||
<span class="badge-modify">MODIFY</span>
|
||
<span class="path">xAiApi/Providers/XAIServiceBase.cs</span>
|
||
</div>
|
||
|
||
<pre>// ۱. افزودن فیلد و تزریق در Constructor
|
||
private readonly IXFileProvider _fileProvider;
|
||
|
||
protected XAIServiceBase(
|
||
IXAiDataProvider dataProvider,
|
||
ILogger<XAIServiceBase> logger,
|
||
XAiApiConfiguration configuration,
|
||
XValidationProvider validationProvider,
|
||
IXFileProvider fileProvider, // ✅ جدید
|
||
string model = null
|
||
)
|
||
{
|
||
this.dataProvider = dataProvider;
|
||
this.logger = logger;
|
||
this.configuration = configuration;
|
||
this.validationProvider = validationProvider;
|
||
this._fileProvider = fileProvider; // ✅ مقداردهی
|
||
|
||
Descriptor = configuration.GetModel(model);
|
||
Options = new ChatOptions();
|
||
}
|
||
|
||
// ۲. بهروزرسانی امضای متد AskAsync در Interface و Implementation
|
||
public async Task<XAiMessageDto> AskAsync(
|
||
string prompt,
|
||
string ownerId,
|
||
Guid projectId,
|
||
Guid conversationId,
|
||
string connectionId = null,
|
||
IEnumerable<XFileDto> attachedFiles = null, // ✅ تغییر از IFormFileCollection
|
||
CancellationToken cancellationToken = default
|
||
)
|
||
{
|
||
// ... (کدهای اعتبارسنجی و دریافت Project/Conversation)
|
||
|
||
// ✅ پردازش فایلهای ضمیمه
|
||
var fileContents = await ProcessAttachedFilesAsync(attachedFiles, cancellationToken);
|
||
|
||
// ... (ساخت promptMessage)
|
||
|
||
// ✅ ذخیره ارجاع فایلها در MetaDatas پیام
|
||
if (attachedFiles != null && attachedFiles.Any())
|
||
{
|
||
var fileIds = attachedFiles.Select(f => f.Id).ToList();
|
||
promptMessage.MetaDatas = new Dictionary<string, object>
|
||
{
|
||
{ "AttachedFileIds", fileIds }
|
||
}.ToJSON();
|
||
}
|
||
|
||
promptMessage = await dataProvider.AddMessage(...);
|
||
|
||
// ✅ الحاق محتوا به ChatMessage
|
||
var promptChatMessage = promptMessage.ToChatMessages(fileContents);
|
||
|
||
var answer = await AskLLMAsync(history: history, prompt: promptChatMessage, cancellationToken: cancellationToken);
|
||
|
||
// ... (ذخیره پاسخ و بازگشت نتیجه)
|
||
}</pre>
|
||
</div>
|
||
|
||
<!-- Step 3 -->
|
||
<div class="section" id="step3">
|
||
<h2>🔍 گام ۴: پیادهسازی منطق استخراج محتوا از فایل</h2>
|
||
<p>این متد کمکی درون <code>XAiServiceBase</code> مسئول خواندن فایل از طریق <code>XFileProvider</code> و تبدیل آن به فرمت قابل فهم برای LLM است.</p>
|
||
|
||
<div class="file-change">
|
||
<span class="badge-new">NEW METHOD</span>
|
||
<span class="path">xAiApi/Providers/XAIServiceBase.cs</span>
|
||
</div>
|
||
|
||
<pre>/// <summary>
|
||
/// پردازش فایلهای ضمیمه و تبدیل به AIContent
|
||
/// </summary>
|
||
private async Task<IList<AIContent>> ProcessAttachedFilesAsync(
|
||
IEnumerable<XFileDto> files,
|
||
CancellationToken cancellationToken = default
|
||
)
|
||
{
|
||
var contents = new List<AIContent>();
|
||
|
||
if (files == null || !files.Any())
|
||
{
|
||
return contents;
|
||
}
|
||
|
||
foreach (var file in files)
|
||
{
|
||
try
|
||
{
|
||
// ✅ دریافت استریم فایل از ماژول xFileService
|
||
var descriptor = await _fileProvider.GetFileDescriptor(
|
||
id: file.Id,
|
||
cancellationToken: cancellationToken
|
||
);
|
||
|
||
if (descriptor == null || descriptor.Stream == null)
|
||
{
|
||
logger.LogWarning("فایل {FileName} یافت نشد یا قابل خواندن نیست.", file.FileName);
|
||
continue;
|
||
}
|
||
|
||
// ✅ تشخیص نوع فایل و پردازش مناسب
|
||
if (file.Type == XFileType.Image || descriptor.MIMEType.StartsWith("image/"))
|
||
{
|
||
// برای مدلهای Vision: ارسال به صورت DataContent
|
||
using var memoryStream = new MemoryStream();
|
||
await descriptor.Stream.CopyToAsync(memoryStream, cancellationToken);
|
||
contents.Add(new DataContent(memoryStream.ToArray(), descriptor.MIMEType));
|
||
}
|
||
else
|
||
{
|
||
// برای فایلهای متنی: خواندن محتوا و الحاق به Prompt
|
||
using var reader = new StreamReader(descriptor.Stream);
|
||
var textContent = await reader.ReadToEndAsync(cancellationToken);
|
||
|
||
// قالببندی برای درک بهتر مدل از منبع متن
|
||
var formattedText = $"[File: {file.FileName} (Type: {descriptor.MIMEType})]\n{textContent}\n[/File]";
|
||
contents.Add(new TextContent(formattedText));
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "خطا در پردازش فایل ضمیمه: {FileName}", file.FileName);
|
||
}
|
||
}
|
||
|
||
return contents;
|
||
}</pre>
|
||
<div class="alert alert-success">
|
||
<strong>✅ مزیت:</strong> با استفاده از <code>GetFileDescriptor</code>، ماژول هوش مصنوعی نیازی به دانستن جزئیات سیستم فایل (File System) ندارد و کاملاً از <code>xFileService</code> انتزاع یافته است.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 4 -->
|
||
<div class="section" id="step4">
|
||
<h2>🔌 گام ۵: بهروزرسانی Extension مدلها</h2>
|
||
<p>متد <code>ToChatMessages</code> باید بتواند محتوای استخراج شده از فایلها را در کنار متن اصلی پیام قرار دهد.</p>
|
||
|
||
<div class="file-change">
|
||
<span class="badge-modify">MODIFY</span>
|
||
<span class="path">xAiModels/Extensions/XAiModelsExtensions.cs</span>
|
||
</div>
|
||
|
||
<pre>public static ChatMessage ToChatMessages(
|
||
this XAiMessageDto source,
|
||
IList<AIContent> additionalContents = null // ✅ پارامتر جدید
|
||
)
|
||
{
|
||
ChatMessage result = null;
|
||
|
||
if (!source.IsNullOrDefault())
|
||
{
|
||
var contents = new List<AIContent>();
|
||
|
||
// ۱. افزودن متن اصلی پیام (Prompt کاربر)
|
||
if (!string.IsNullOrWhiteSpace(source.Content))
|
||
{
|
||
contents.Add(new TextContent(source.Content));
|
||
}
|
||
|
||
// ۲. ✅ افزودن محتوای استخراج شده از فایلها
|
||
if (additionalContents != null && additionalContents.Any())
|
||
{
|
||
contents.AddRange(additionalContents);
|
||
}
|
||
|
||
result = new ChatMessage
|
||
{
|
||
AuthorName = source.Role == XAiChatRole.User && !source.Owner.IsNullOrDefault()
|
||
? source.Owner.GetFullname()
|
||
: string.Empty,
|
||
Role = source.Role.ToChatRole(),
|
||
MessageId = source.Id.ToString(),
|
||
Contents = contents // ✅ لیست ترکیبی از متن و فایل
|
||
};
|
||
}
|
||
|
||
return result;
|
||
}</pre>
|
||
</div>
|
||
|
||
<!-- Step 5 -->
|
||
<div class="section" id="step5">
|
||
<h2>🔗 گام ۶: ثبت وابستگیها (Dependency Injection)</h2>
|
||
<p>اطمینان حاصل کنید که <code>IXFileProvider</code> در کانتینر DI ثبت شده است (که بر اساس فایلهای ارائه شده، قبلاً در <code>xFileService.DI.XDIHelperExtension</code> انجام شده است). فقط باید اطمینان حاصل کنیم که در <code>xAiApi</code> قابل تزریق است.</p>
|
||
|
||
<div class="file-change">
|
||
<span class="badge-modify">MODIFY</span>
|
||
<span class="path">xAiApi/Startup.cs</span>
|
||
</div>
|
||
|
||
<pre>public void ConfigureServices(IServiceCollection services)
|
||
{
|
||
// ... (سایر ثبتها)
|
||
|
||
// ✅ اطمینان از ثبت سرویس فایل (اگر قبلاً در ماژول xFileService ثبت نشده، اینجا فراخوانی شود)
|
||
// services.AddXFileService<XAiApiDbContext>(xDataService.Constants.XRepositoryType.EF);
|
||
|
||
// ✅ بهروزرسانی ثبت سرویسهای AI برای تزریق IXFileProvider
|
||
// نکته: چون XAIServiceBase کلاس پایه است، باید در کلاسهای مشتق شده (مثل XDefaultAiService) تزریق شود.
|
||
|
||
// مثال برای XDefaultAiService:
|
||
// services.AddScoped<IXDefaultAiService>(sp => new XDefaultAiService(
|
||
// sp.GetRequiredService<IXAiDataProvider>(),
|
||
// sp.GetRequiredService<ILogger<XDefaultAiService>>(),
|
||
// sp.GetRequiredService<XAiApiConfiguration>(),
|
||
// sp.GetRequiredService<XValidationProvider>(),
|
||
// sp.GetRequiredService<IXFileProvider>() // ✅ تزریق جدید
|
||
// ));
|
||
}</pre>
|
||
<div class="alert alert-warning">
|
||
<strong>⚠️ توجه:</strong> اگر <code>XAiServiceBase</code> را مستقیماً ثبت نمیکنید و از کلاسهای مشتق شده استفاده میکنید، باید Constructor آن کلاسها را نیز برای پذیرش <code>IXFileProvider</code> و پاس دادن آن به <code>base(...)</code> بهروزرسانی کنید.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Flow -->
|
||
<div class="section" id="flow">
|
||
<h2>🔄 گام ۷: جریان کامل پردازش</h2>
|
||
<div class="flow-diagram">
|
||
<span class="flow-step">📤 کلاینت: ارسال Form (Prompt + Files)</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">🎮 Controller: فراخوانی IXFileProvider.Upload</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">💾 xFileService: ذخیره فیزیکی و ثبت در DB</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">⚙️ XAiServiceBase: دریافت List<XFileDto></span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">🔍 XAiServiceBase: فراخوانی GetFileDescriptor</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">📝 تبدیل Stream به TextContent/DataContent</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">🤖 ارسال ChatMessage (Text + Files) به LLM</span>
|
||
<span class="flow-arrow">→</span>
|
||
<span class="flow-step">💾 ذخیره Message با FileIds در MetaDatas</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Summary -->
|
||
<div class="section" id="summary">
|
||
<h2>📋 گام ۸: خلاصه تغییرات</h2>
|
||
<table>
|
||
<tr>
|
||
<th>ردیف</th>
|
||
<th>فایل / ماژول</th>
|
||
<th>نوع تغییر</th>
|
||
<th>توضیح</th>
|
||
</tr>
|
||
<tr>
|
||
<td>۱</td>
|
||
<td><code>xAiApi/Controllers/XAiServiceControllerBase.cs</code></td>
|
||
<td><span class="badge-modify">MODIFY</span></td>
|
||
<td>افزودن <code>IXFileProvider</code> و فراخوانی <code>Upload</code> قبل از سرویس AI</td>
|
||
</tr>
|
||
<tr>
|
||
<td>۲</td>
|
||
<td><code>xAiApi/Interfaces/IXAiServiceBase.cs</code></td>
|
||
<td><span class="badge-modify">MODIFY</span></td>
|
||
<td>تغییر پارامتر <code>files</code> از <code>IFormFileCollection</code> به <code>IEnumerable<XFileDto></code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>۳</td>
|
||
<td><code>xAiApi/Providers/XAIServiceBase.cs</code></td>
|
||
<td><span class="badge-modify">MODIFY</span></td>
|
||
<td>تزریق <code>IXFileProvider</code> و افزودن متد <code>ProcessAttachedFilesAsync</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>۴</td>
|
||
<td><code>xAiModels/Extensions/XAiModelsExtensions.cs</code></td>
|
||
<td><span class="badge-modify">MODIFY</span></td>
|
||
<td>پشتیبانی <code>ToChatMessages</code> از <code>additionalContents</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>۵</td>
|
||
<td><code>xAiApi/Providers/XDefaultAiService.cs</code> (و سایر مشتقها)</td>
|
||
<td><span class="badge-modify">MODIFY</span></td>
|
||
<td>بهروزرسانی Constructor برای پاس دادن <code>IXFileProvider</code> به کلاس پایه</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<div class="alert alert-success">
|
||
<strong>✅ دستاوردهای این طراحی:</strong>
|
||
<ul style="padding-right: 25px; margin-top: 10px;">
|
||
<li>🛡️ <strong>جداسازی مسئولیتها:</strong> ماژول AI دیگر درگیر آپلود یا مدیریت فایل فیزیکی نیست.</li>
|
||
<li>♻️ <strong>استفاده مجدد:</strong> از تمام قابلیتهای <code>xFileService</code> (مانند Thumbnail، References، و Storage) بهره میبریم.</li>
|
||
<li>🔗 <strong>ردیابیپذیری:</strong> با ذخیره <code>FileId</code> در <code>MetaDatas</code>، همیشه میتوانیم بفهمیم کدام فایلها به کدام پیام متصل بودهاند.</li>
|
||
<li>🎨 <strong>پشتیبانی چندوجهی (Multi-modal):</strong> آمادهسازی برای ارسال تصاویر به صورت <code>DataContent</code> به مدلهای Vision.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="footer">
|
||
<p><strong>👨💻 توسعهدهنده:</strong> هادی خزاعی اصل</p>
|
||
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
|
||
<p><strong>📅 تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</p>
|
||
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
|
||
📎 پیادهسازی استخراج محتوا با ماژول xFileService - تمامی حقوق محفوظ است
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html> |