kate: replace ollie-9p complete with generate for code completions

- Build the completion prompt inline (mirrors agent/complete.go prompt)
- Call ollie-9p rdwr generate instead of ollie-9p rdwr complete
- Pass backend/model as JSON fields in the generate request
- StripCompletionNoise and stripPrefixEcho moved into C++ helpers
This commit is contained in:
Levi Neely 2026-08-07 07:37:42 +02:00
parent ab8aa8d719
commit 08ec46027e
1 changed files with 73 additions and 20 deletions

View File

@ -313,33 +313,45 @@ void OllieGhostProvider::requestCompletion()
m_triggerCursor = cursor;
m_requesting = true;
// Build context with optional backend/model override
QString context;
if (m_plugin) {
QString backend = m_plugin->completionBackend();
QString model = m_plugin->completionModel();
if (!backend.isEmpty())
context += QStringLiteral("backend=") + backend + QStringLiteral("\n");
if (!model.isEmpty())
context += QStringLiteral("model=") + model + QStringLiteral("\n");
}
// Trim prefix/suffix to limits matching agent/complete.go
const int prefixMax = 12000;
const int suffixMax = 1000;
if (prefix.size() > prefixMax)
prefix = prefix.right(prefixMax);
if (suffix.size() > suffixMax)
suffix = suffix.left(suffixMax);
// Build JSON request for ollie-9p rdwr complete
// Build the completion prompt
QString fileHint;
if (!filePath.isEmpty())
fileHint = " in " + filePath;
QString contextBlock;
if (!cwd.isEmpty())
contextBlock = "\nWorking directory: " + cwd;
QString prompt = QString(
"Implement the code at the cursor%2. The prefix ends at the point where new code is needed. Write the implementation — do not echo stubs, TODOs, or placeholder returns from the prefix. Output ONLY raw code. No reasoning, no shell commands, no explanations, no markdown fences, no backticks, no preamble. Your entire response must be valid code that can be inserted directly into the file.%1"
"<prefix>\n%3\n</prefix>\n<suffix>\n%4\n</suffix>"
).arg(contextBlock).arg(fileHint).arg(prefix).arg(suffix);
// Build JSON request for ollie-9p rdwr generate
QJsonObject request;
request["file"] = filePath;
request["prefix"] = prefix;
request["suffix"] = suffix;
if (!context.isEmpty()) {
request["context"] = context;
}
request["prompt"] = prompt;
QString backendName = m_plugin ? m_plugin->completionBackend() : QString();
QString modelName = m_plugin ? m_plugin->completionModel() : QString();
if (!backendName.isEmpty())
request["backend"] = backendName;
if (!modelName.isEmpty())
request["model"] = modelName;
QJsonDocument docJson(request);
QString jsonStr = QString::fromUtf8(docJson.toJson(QJsonDocument::Compact));
// Run ollie-9p rdwr complete
// Run ollie-9p rdwr generate
QProcess *process = new QProcess(this);
process->setProgram("ollie-9p");
process->setArguments({"rdwr", "complete"});
process->setArguments({"rdwr", "generate"});
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) {
@ -367,7 +379,15 @@ void OllieGhostProvider::onCompletionReply(const QString &result)
if (m_view->cursorPosition() != m_triggerCursor)
return;
QStringList lines = result.split(QStringLiteral("\n"));
// Strip completion noise (safety net — prompt asks for raw code only)
QString stripped = stripCompletionNoise(result);
// Strip prefix echo (model may repeat trailing prefix chars)
stripped = stripPrefixEcho(stripped);
if (stripped.isEmpty())
return;
QStringList lines = stripped.split(QStringLiteral("\n"));
// Append to history and advance index
m_history.append(lines);
@ -489,3 +509,36 @@ QString OllieGhostProvider::gatherSuffix() const
KTextEditor::Range suffixRange(cursor, endCursor);
return doc->text(suffixRange);
}
// === Completion post-processing helpers (mirrors agent/complete.go) ===
static QString stripCompletionNoise(const QString &s)
{
QStringList lines = s.split('\n');
QStringList filtered;
for (const QString &line : lines) {
QString trimmed = line.trimmed();
if (trimmed.startsWith("```"))
continue;
if (trimmed == "<prefix>" || trimmed == "</prefix>" ||
trimmed == "<suffix>" || trimmed == "</suffix>")
continue;
if (trimmed.startsWith(":: "))
continue;
filtered.append(line);
}
return filtered.join('\n');
}
static QString stripPrefixEcho(const QString &prefix, const QString &result)
{
int tailMax = 200;
if (prefix.size() < tailMax)
tailMax = prefix.size();
for (int i = tailMax; i > 0; --i) {
QString tail = prefix.right(i);
if (result.startsWith(tail))
return result.mid(i);
}
return result;
}