deel-local-cli 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,14 +47,16 @@ export function 배울것(message) {
47
47
  // ── 1) 컨텍스트 한계 ──────────────────────────────────────────────────
48
48
  // OpenAI·vLLM·LiteLLM·대부분의 게이트웨이가 이 모양으로 말한다.
49
49
  const ctx표 = [
50
+ // OpenAI·대부분의 게이트웨이
50
51
  // "maximum context length is 8192 tokens, however you requested 41003"
51
52
  // "…is 4096 tokens. However, you requested 5000 tokens." ← 마침표가 중간에 낀다.
52
53
  // 그래서 [^.] 로 막으면 안 된다. 대신 사이를 80자로만 묶어 엉뚱한 숫자를 안 집게 한다.
53
54
  /maximum context length is\s+(\d+)\s*tokens?(?:[\s\S]{0,80}?requested\s+(\d+))?/i,
54
55
  /context length[^.\d]{0,40}(\d{3,})[\s\S]{0,80}?requested\s+(\d+)/i,
55
- // llama.cpp: "the request exceeds the available context size. try increasing the context size or enable context shift"
56
- // 숫자를 안 주는 경우다. n_ctx = 8192 처럼 따로 적어 주기도 한다.
57
- /n_ctx\s*[:=]?\s*(\d{3,})/i,
56
+ // vLLM: "Requested tokens (41003) exceed context window of 8192"
57
+ /context window of\s+(\d{3,})/i,
58
+ // llama.cpp: "n_ctx = 8192" · "greater than n_ctx (8192)" ← 괄호가 붙는다
59
+ /n_ctx\s*[:=]?\s*\(?\s*(\d{3,})/i,
58
60
  // 한국어로 옮겨 놓은 사내 게이트웨이
59
61
  /(?:최대\s*)?컨텍스트[^\d]{0,20}(\d{3,})/,
60
62
  ];
@@ -83,6 +85,41 @@ export function 배울것(message) {
83
85
  return { kind: 'out', limit, asked: null, text: 짧게(s) };
84
86
  }
85
87
 
88
+ /*
89
+ * ── 모르는 서버를 위한 마지막 수 ──────────────────────────────────────
90
+ *
91
+ * 위의 표들은 '이미 본 문장' 만 읽는다. 그런데 이 파일이 존재하는 이유는
92
+ * **처음 보는 서버에서도 통하게** 하려는 것이다. 표를 늘리는 방식으로는
93
+ * 영영 못 따라간다 — 서버는 계속 새로 나오고, 문장은 저마다 다르다.
94
+ *
95
+ * TGI "input length 41003 exceeds maximum 8192"
96
+ * llama.cpp "context the overflows: 41003 > 8192"
97
+ * KoboldCpp "Prompt is too long: 41003 > 8192"
98
+ *
99
+ * 문장은 다 다른데 **모양은 하나**다 — 숫자가 둘 나오고, 우리가 보낸 큰
100
+ * 값과 서버가 받는 작은 값이다. 그러면 규칙은 간단하다: 길이가 문제라고
101
+ * 말하는 문장에서 숫자를 다 뽑아, **작은 쪽이 한계**다.
102
+ *
103
+ * 틀려도 안전한 쪽으로 틀린다 — 한계를 실제보다 작게 잡으면 조금 손해 보고
104
+ * 돌아갈 뿐이지만, 크게 잡으면 조용히 잘린다. 그리고 다음 요청에서 서버가
105
+ * 또 알려 주므로 스스로 고쳐진다.
106
+ *
107
+ * **표를 먼저 다 본 뒤에** 온다. 순서가 중요하다 — 앞에 두면
108
+ * `max_tokens is too large: 200000 … at most 16384` 같은 **출력** 한계가
109
+ * 컨텍스트 한계로 잘못 배워진다. 그러면 창을 16,384 로 줄여 버린다.
110
+ */
111
+ if (길이문제인가(s)) {
112
+ const 숫자들 = [...s.matchAll(/\d[\d,]*/g)]
113
+ .map((m) => 성한수(m[0].replace(/,/g, '')))
114
+ .filter(Boolean);
115
+ if (숫자들.length >= 2) {
116
+ const 작은것 = Math.min(...숫자들);
117
+ const 큰것 = Math.max(...숫자들);
118
+ // 둘이 같으면 뭘 뽑은 건지 알 수 없다. 그때는 배웠다고 하지 않는다.
119
+ if (작은것 < 큰것) return { kind: 'ctx', limit: 작은것, asked: 큰것, text: 짧게(s), 짐작: true };
120
+ }
121
+ }
122
+
86
123
  return null;
87
124
  }
88
125
 
@@ -94,7 +131,12 @@ export function 배울것(message) {
94
131
  */
95
132
  export function 길이문제인가(message) {
96
133
  const s = String(message ?? '');
97
- return /context (?:length|size|window)|too (?:long|large|many tokens)|exceeds?[^.]{0,30}(?:context|limit|token)|max_tokens|token limit|컨텍스트|너무 (?:깁|많|큽)/i.test(s);
134
+ return /context (?:length|size|window)|too (?:long|large|many tokens)|exceeds?[^.]{0,30}(?:context|limit|token)|max_tokens|token limit|컨텍스트|너무 (?:깁|많|큽)/i.test(s)
135
+ // 아래는 서버마다 말이 달라 위 표에 안 잡히던 것들이다. 전부 실제 문장이다.
136
+ // TGI "input length 41003 exceeds maximum 8192"
137
+ // llama.cpp "context the overflows: 41003 > 8192"
138
+ // 여러 곳 "prompt is too long" · "n_ctx" · "n_predict"
139
+ || /overflow|input length|prompt (?:is )?too|exceeds?\s+maximum|n_ctx|n_predict|입력이 (?:너무|깁)/i.test(s);
98
140
  }
99
141
 
100
142
  function 짧게(s) {
package/src/commands.js CHANGED
@@ -29,6 +29,7 @@ export const COMMANDS = {
29
29
  context: { desc: '컨텍스트 사용량 보기' },
30
30
  ctx: { desc: '컨텍스트 길이 — 모델에 맞춰 다시 재거나 직접 지정', arg: '[auto|숫자|640k]' },
31
31
  out: { desc: '한 번에 받을 답 길이 상한 — 큰 파일이 잘리면 여기를 올린다', arg: '[숫자|32k|auto]' },
32
+ grade: { desc: '모델 급 — 얼마나 알아서 하나. 창 크기와는 다른 축', arg: '[작음|보통|큼|auto]' },
32
33
  compact: { desc: '오래된 대화 줄이기' },
33
34
  model: { desc: '연결·모델 바꾸기 (이름 일부 · list · models)', arg: '[이름|list|models]' },
34
35
  think: { desc: '추론 강도 (off/low/medium/high/max)', arg: '<수준>' },
@@ -106,6 +107,10 @@ export async function handle(line, session, ctx) {
106
107
 
107
108
  case 'ctx': return await ctxLength(session, arg), { handled: true };
108
109
 
110
+ // 모델 급. /ctx 와 헷갈리기 쉬워 설명에서 못을 박는다 —
111
+ // /ctx 는 '얼마나 담나', /grade 는 '얼마나 알아서 하나' 다.
112
+ case 'grade': case '급': return 모델급(session, arg), { handled: true };
113
+
109
114
  // 한 번에 받을 답 길이. 컨텍스트(/ctx)와는 다른 축이라 명령을 따로 둔다 —
110
115
  // /ctx out 안에 숨겨 두니 아무도 못 찾았고, 정작 큰 파일이 안 만들어지는 원인이었다.
111
116
  case 'out': case '출력': return await 출력상한(session, arg), { handled: true };
@@ -1042,6 +1047,74 @@ async function 출력상한(session, arg = '') {
1042
1047
  say('');
1043
1048
  }
1044
1049
 
1050
+ /**
1051
+ * /grade — 모델 급.
1052
+ *
1053
+ * `/ctx` 와는 **다른 축**이다. 헷갈리기 쉬워서 화면에서도 나란히 보여 준다.
1054
+ * /ctx 얼마나 담나 (창 크기)
1055
+ * /grade 얼마나 알아서 하나 (능력)
1056
+ *
1057
+ * 창이 128k 인 3B 모델이 있고, 창이 32k 인 아주 좋은 모델도 있다. 둘을 같은
1058
+ * 값으로 다루면 하나는 붙들려 있고 하나는 놓쳐진다.
1059
+ *
1060
+ * 평소에는 안 건드려도 된다 — 이름으로 짐작하고, 대화가 돌수록 실제로 본 것
1061
+ * (인자 잘림·빈 답·편집 실패·되풀이)으로 고쳐 잡는다. 여기서 정하면 그것이
1062
+ * 이기고, `auto` 로 되돌리면 다시 스스로 잡는다.
1063
+ */
1064
+ function 모델급(session, arg = '') {
1065
+ const 값 = String(arg ?? '').trim().toLowerCase();
1066
+ const 별명 = {
1067
+ '작음': '작음', 'small': '작음', 's': '작음', '작': '작음',
1068
+ '보통': '보통', 'medium': '보통', 'm': '보통', '중': '보통',
1069
+ '큼': '큼', 'large': '큼', 'l': '큼', 'big': '큼', '대': '큼',
1070
+ };
1071
+
1072
+ if (값 === 'auto' || 값 === '자동') {
1073
+ session.급정한것 = null;
1074
+ const g = session.급();
1075
+ say('');
1076
+ say(` ${c.cyan('◈')} 모델 급을 다시 ${c.bold('스스로 잡게')} 했습니다 — 지금은 ${c.white(g.급)}`);
1077
+ say(` ${c.gray(g.왜)}`);
1078
+ return;
1079
+ }
1080
+
1081
+ if (값 && 별명[값]) {
1082
+ session.급정한것 = 별명[값];
1083
+ const v = session.급값();
1084
+ say('');
1085
+ say(` ${c.cyan('◈')} 모델 급을 ${c.bold(별명[값])} 으로 정했습니다.`);
1086
+ say(` ${c.gray(`한 번에 만들 파일 ${v.한번에쓸파일}개 · ${v.나눠쓰기줄}줄 넘으면 나눠 쓰기`)}`);
1087
+ say(` ${c.gray('/grade auto 로 되돌리면 다시 스스로 잡습니다.')}`);
1088
+ return;
1089
+ }
1090
+
1091
+ // 인자가 없으면 지금 상태를 보여 준다.
1092
+ const g = session.급();
1093
+ const v = session.급값();
1094
+ const 본 = session.본것;
1095
+ say('');
1096
+ say(` ${c.bold('모델 급')} ${c.hcyan(g.급)}${g.짐작 ? c.gray(' (짐작)') : ''}`);
1097
+ say(` ${c.gray(g.왜)}`);
1098
+ say('');
1099
+ say(` ${c.gray('이 급에서 쓰는 값')}`);
1100
+ say(` ${c.gray('한 번에 만들 파일')} ${c.white(String(v.한번에쓸파일))}개`);
1101
+ say(` ${c.gray('나눠 쓰기 기준')} ${c.white(String(v.나눠쓰기줄))}줄`);
1102
+ say(` ${c.gray('절차를 못 박나')} ${v.절차를못박나 ? c.white('예') : c.gray('아니오 — 목표만 준다')}`);
1103
+ say(` ${c.gray('하위 작업 권함')} ${v.하위작업권함 ? c.white('예') : c.gray('아니오')}`);
1104
+ if (본 && 본.걸음) {
1105
+ say('');
1106
+ say(` ${c.gray('이번 대화에서 실제로 본 것')} ${c.gray(`(${본.걸음}걸음)`)}`);
1107
+ const 줄 = [
1108
+ ['인자 잘림', 본.잘린인자], ['빈 답', 본.빈답],
1109
+ ['편집 실패', 본.편집실패], ['되풀이', 본.되풀이], ['도구 성공', 본.도구성공],
1110
+ ];
1111
+ say(' ' + 줄.map(([이름, n]) => `${c.gray(이름)} ${n ? c.white(String(n)) : c.gray('0')}`).join(c.gray(' · ')));
1112
+ }
1113
+ say('');
1114
+ say(` ${c.gray('/ctx 와는 다른 축입니다 — /ctx 는 얼마나 담나, /grade 는 얼마나 알아서 하나.')}`);
1115
+ say(` ${c.gray('직접 정하려면 /grade 작음|보통|큼 · 되돌리려면 /grade auto')}`);
1116
+ }
1117
+
1045
1118
  /**
1046
1119
  * 컨텍스트 길이를 보고·다시 재고·직접 지정한다.
1047
1120
  *