ref: 6ff6f63950fa65e7d3ca0981f572bb31ac328796
parent: 3b49941153a3ca965ba574d9e64f0325383316fd
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Dec 30 13:50:38 EST 2025
adds ocomplete (acme)
--- a/README
+++ b/README
@@ -9,13 +9,22 @@
(play around with the detail values until you get a stable environment)
+TOOLS:
+
+- oai: simple shell-like chat between user and assistant
+- ocomplete: acme interface.
+
+
USAGE:
oai [-k apikey] [-m model] baseurl
+ocomplete [-k apikey] [-m model] baseurl
baseurl is the http url without the v1/... stuff, with llama-server this is usually just http://server:8080.
After that, you get a user: prompt for your user messages.
+
+Ocomplete: Call the program from within an acme window with some selected text. The whole window contents will be sent to the API as context, and the LLM response will be appended to the selected text.
LIBRARY:
--- a/mkfile
+++ b/mkfile
@@ -1,6 +1,6 @@
</$objtype/mkfile
-TARG=oai
+TARG=oai ocomplete
HFILES=oai.h
OFILES=oailib.$O
--- a/oai.h
+++ b/oai.h
@@ -20,3 +20,4 @@
int initoai(char *baseurl, char *apikey);
OResult makerequest(ORequest);
+int addprompt(ORequest*, char *role, char *content, ...);
--- a/oailib.c
+++ b/oailib.c
@@ -184,3 +184,30 @@
return ret;
}
+
+int
+addprompt(ORequest *r, char *role, char *content, ...)
+{+ OPrompt *p;
+ char *s;
+ va_list arg;
+
+ va_start(arg, content);
+ s = vsmprint(content, arg);
+ va_end(arg);
+
+ if (!r->prompts) {+ p = mallocz(sizeof(OPrompt), 1);
+ r->prompts = p;
+ goto Fill;
+ }
+ for (p = r->prompts; p->next; p = p->next)
+ ;
+ p->next = mallocz(sizeof(OPrompt), 1);
+ p = p->next;
+
+Fill:
+ p->role = strdup(role);
+ p->content = s;
+ return 1;
+}
--- /dev/null
+++ b/ocomplete.c
@@ -1,0 +1,78 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "oai.h"
+
+static void
+usage(void)
+{+ fprint(2, "usage: %s [-k apikey] [-m model] baseurl\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc, char **argv)
+{+ char buf[128];
+ ORequest req;
+ OResult res;
+ char *key = nil;
+ char *body, *rdsel;
+ Biobuf *bodyin, *selio;
+ char *winid;
+
+ req.prompts = nil;
+ req.model = nil;
+
+ ARGBEGIN{+ case 'h':
+ usage();
+ case 'k':
+ key = EARGF(usage());
+ break;
+ case 'm':
+ req.model = EARGF(usage());
+ break;
+ }ARGEND;
+
+ if (argc != 1)
+ usage();
+
+ winid = getenv("winid");+ if (!winid || winid[0] == 0)
+ sysfatal("not in acme");+
+ if (!initoai(argv[0], key))
+ sysfatal("initoai: %r");+
+ snprint(buf, sizeof buf, "/mnt/acme/%s/body", winid);
+ bodyin = Bopen(buf, OREAD);
+ snprint(buf, sizeof buf, "/mnt/acme/%s/rdsel", winid);
+ selio = Bopen(buf, OREAD);
+
+ if (!(bodyin && selio))
+ sysfatal("error opening files: %r");+
+ body = Brdstr(bodyin, 0, 1);
+ rdsel = Brdstr(selio, 0, 1);
+
+ Bterm(bodyin);
+ Bterm(selio);
+
+ if (!(body && rdsel))
+ sysfatal("read: %r");+
+ addprompt(&req, "system", "You are the Acme completion assistant. Your task is to append matching content to the user prompt, based on the context of the given file. The user prompt is part of the file contents, and your response will be appended there. No further discussion, just this completion. Do not repeat the user prompt. The file contents are:\n\n%s", body);
+ addprompt(&req, "user", rdsel);
+
+ res = makerequest(req);
+ if (!res.message)
+ sysfatal("invalid result");+
+ snprint(buf, sizeof buf, "/mnt/acme/%s/wrsel", winid);
+ selio = Bopen(buf, OWRITE);
+ Bprint(selio, "%s%s", rdsel, res.message);
+ Bterm(selio);
+
+ exits(nil);
+}
--
⑨