* @brief Fetches a Serial Studio help page asynchronously and feeds the result back via * recordToolResult + resumeAfterToolBatch. */
| 1504 | * recordToolResult + resumeAfterToolBatch. |
| 1505 | */ |
| 1506 | void AI::Conversation::fetchHelpPage(const QString& callId, const QString& path) |
| 1507 | { |
| 1508 | static const QString kHelpBase = QStringLiteral("https://raw.githubusercontent.com/Serial-Studio/" |
| 1509 | "Serial-Studio/master/doc/help/"); |
| 1510 | |
| 1511 | QUrl url; |
| 1512 | if (path.startsWith(QStringLiteral("http"), Qt::CaseInsensitive)) { |
| 1513 | url = QUrl(path); |
| 1514 | } else { |
| 1515 | QString page = path; |
| 1516 | if (page.startsWith('/')) |
| 1517 | page.remove(0, 1); |
| 1518 | |
| 1519 | if (page.isEmpty()) |
| 1520 | page = QStringLiteral("Home"); |
| 1521 | |
| 1522 | if (!page.endsWith(QStringLiteral(".md"), Qt::CaseInsensitive)) |
| 1523 | page += QStringLiteral(".md"); |
| 1524 | |
| 1525 | url = QUrl(kHelpBase + page); |
| 1526 | } |
| 1527 | |
| 1528 | const auto host = url.host(); |
| 1529 | const bool allowed = host.endsWith(QStringLiteral("githubusercontent.com")) |
| 1530 | || host.endsWith(QStringLiteral("github.com")) |
| 1531 | || host.endsWith(QStringLiteral("serial-studio.com")); |
| 1532 | |
| 1533 | if (!url.isValid() || !allowed) { |
| 1534 | QJsonObject err; |
| 1535 | err[QStringLiteral("ok")] = false; |
| 1536 | err[QStringLiteral("error")] = QStringLiteral("Only github.com / raw.githubusercontent.com / " |
| 1537 | "serial-studio.com URLs are allowed"); |
| 1538 | err[QStringLiteral("url")] = url.toString(); |
| 1539 | recordToolResult(callId, QStringLiteral("meta.fetchHelp"), err); |
| 1540 | updateToolCallCard(callId, CallStatus::Error, err); |
| 1541 | releaseOutstandingToolResult(); |
| 1542 | if (m_outstandingToolResults == 0 && m_awaitingConfirm.isEmpty() |
| 1543 | && (!m_reply || m_reply == nullptr)) |
| 1544 | resumeAfterToolBatch(); |
| 1545 | return; |
| 1546 | } |
| 1547 | |
| 1548 | qCDebug(serialStudioAI) << "meta.fetchHelp" << url.toString(); |
| 1549 | |
| 1550 | QNetworkRequest req(url); |
| 1551 | req.setRawHeader("User-Agent", "SerialStudio-AIAssistant"); |
| 1552 | req.setRawHeader("Accept", "text/markdown,text/plain;q=0.9,text/html;q=0.5"); |
| 1553 | req.setTransferTimeout(15 * 1000); |
| 1554 | auto* reply = m_helpFetchNam.get(req); |
| 1555 | |
| 1556 | connect(reply, &QNetworkReply::finished, this, [this, callId, reply, url]() { |
| 1557 | completeHelpFetch(callId, url, reply); |
| 1558 | }); |
| 1559 | } |
| 1560 | |
| 1561 | /** |
| 1562 | * @brief Finalizes a meta.fetchHelp request: parses the body, records, resumes. |