mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Socle du renommage jean -> AJEAN, sans aucun impact visible pour le parc installe. Trois briques : - module github.com/nathaninline/ajean, cmd/ajean, internal/ajean, package ajean. Purement interne. - AjeanHome() remplace JeanHome(). Les defauts deviennent %ProgramData%\ajean et /etc/ajean, avec migration du dossier existant au premier lancement. La migration est un os.Rename dans le dossier parent de l'ancien chemin : intra-volume, donc atomique et instantane meme avec des .gguf de plusieurs dizaines de Go. Si le rename echoue (handle ouvert sous Windows, droits, volume en lecture seule) on CONTINUE sur l'ancien chemin et on retentera : jamais de copie, jamais de suppression, le pire cas est "rien n'a bouge". $AJEAN_HOME et $JEAN_HOME restent tous deux honores, ainsi que /etc/default/ajean et /etc/default/jean : un chemin impose a la main est un ordre, on ne le migre pas. - l'auto-MAJ accepte desormais ajean-<os>-<arch> ET jean-<os>-<arch>, dans cet ordre. Indispensable dans les deux sens : les binaires deja installes ne connaissent que jean-*, et ce binaire-ci doit pouvoir s'installer depuis une release anterieure au renommage. Couvert par des tests, dont le repli quand un handle ouvert bloque le rename sous Windows.
122 lines
3.2 KiB
Go
122 lines
3.2 KiB
Go
package ajean
|
|
|
|
// chat_diff.go — petit diff ligne à ligne pour l'interface : quand l'IA modifie
|
|
// un fichier (outil `edit`) ou une page de mémoire (`mem_edit`, `mem_add`), on
|
|
// envoie à l'UI le détail des lignes ajoutées et retirées, qu'elle affiche en
|
|
// vert (+) et rouge (-). Aucune dépendance : LCS classique sur les lignes, avec
|
|
// des garde-fous pour ne jamais transformer un gros remplacement en pavé.
|
|
|
|
import "strings"
|
|
|
|
const (
|
|
diffMaxLines = 400 // au-delà, on compare sans détail (trop gros / trop lent)
|
|
diffMaxShown = 120 // lignes envoyées à l'UI (le reste est résumé)
|
|
)
|
|
|
|
// DiffLine est une ligne de diff : Op vaut " " (contexte), "-" ou "+".
|
|
type DiffLine struct {
|
|
Op string `json:"op"`
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// lineDiff compare deux blocs de texte ligne à ligne. Le résultat garde les
|
|
// lignes communes comme contexte : c'est ce qui rend le changement lisible
|
|
// quand l'IA ne touche qu'un mot au milieu d'un paragraphe.
|
|
func lineDiff(oldText, newText string) []DiffLine {
|
|
a := splitLines(oldText)
|
|
b := splitLines(newText)
|
|
// Blocs énormes : on ne calcule pas la LCS (coût quadratique), on montre
|
|
// simplement l'ancien en retrait et le nouveau en ajout.
|
|
if len(a) > diffMaxLines || len(b) > diffMaxLines {
|
|
out := make([]DiffLine, 0, len(a)+len(b))
|
|
for _, l := range a {
|
|
out = append(out, DiffLine{Op: "-", Text: l})
|
|
}
|
|
for _, l := range b {
|
|
out = append(out, DiffLine{Op: "+", Text: l})
|
|
}
|
|
return capLines(out)
|
|
}
|
|
|
|
// LCS : table des longueurs, puis remontée.
|
|
n, m := len(a), len(b)
|
|
lcs := make([][]int, n+1)
|
|
for i := range lcs {
|
|
lcs[i] = make([]int, m+1)
|
|
}
|
|
for i := n - 1; i >= 0; i-- {
|
|
for j := m - 1; j >= 0; j-- {
|
|
if a[i] == b[j] {
|
|
lcs[i][j] = lcs[i+1][j+1] + 1
|
|
} else if lcs[i+1][j] >= lcs[i][j+1] {
|
|
lcs[i][j] = lcs[i+1][j]
|
|
} else {
|
|
lcs[i][j] = lcs[i][j+1]
|
|
}
|
|
}
|
|
}
|
|
var out []DiffLine
|
|
i, j := 0, 0
|
|
for i < n && j < m {
|
|
switch {
|
|
case a[i] == b[j]:
|
|
out = append(out, DiffLine{Op: " ", Text: a[i]})
|
|
i++
|
|
j++
|
|
case lcs[i+1][j] >= lcs[i][j+1]:
|
|
out = append(out, DiffLine{Op: "-", Text: a[i]})
|
|
i++
|
|
default:
|
|
out = append(out, DiffLine{Op: "+", Text: b[j]})
|
|
j++
|
|
}
|
|
}
|
|
for ; i < n; i++ {
|
|
out = append(out, DiffLine{Op: "-", Text: a[i]})
|
|
}
|
|
for ; j < m; j++ {
|
|
out = append(out, DiffLine{Op: "+", Text: b[j]})
|
|
}
|
|
return capLines(out)
|
|
}
|
|
|
|
// addedDiff présente un contenu entièrement nouveau (création d'une page).
|
|
func addedDiff(text string) []DiffLine {
|
|
lines := splitLines(text)
|
|
out := make([]DiffLine, 0, len(lines))
|
|
for _, l := range lines {
|
|
out = append(out, DiffLine{Op: "+", Text: l})
|
|
}
|
|
return capLines(out)
|
|
}
|
|
|
|
func splitLines(s string) []string {
|
|
s = strings.ReplaceAll(s, "\r\n", "\n")
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
return strings.Split(s, "\n")
|
|
}
|
|
|
|
// capLines borne la taille envoyée à l'UI et signale ce qui a été coupé.
|
|
func capLines(d []DiffLine) []DiffLine {
|
|
if len(d) <= diffMaxShown {
|
|
return d
|
|
}
|
|
cut := len(d) - diffMaxShown
|
|
out := append([]DiffLine{}, d[:diffMaxShown]...)
|
|
return append(out, DiffLine{Op: " ", Text: "…(" + itoa(cut) + " lignes de plus)"})
|
|
}
|
|
|
|
func itoa(n int) string {
|
|
if n == 0 {
|
|
return "0"
|
|
}
|
|
var b []byte
|
|
for n > 0 {
|
|
b = append([]byte{byte('0' + n%10)}, b...)
|
|
n /= 10
|
|
}
|
|
return string(b)
|
|
}
|