Skip to content

Commit 89e53ef

Browse files
committed
Merge branch 'main' into feature/tri-6738-show-aggregated-logs-in-main-page
# Conflicts: # apps/webapp/app/env.server.ts # internal-packages/clickhouse/src/index.ts
2 parents 64e75ea + dfb46d8 commit 89e53ef

File tree

24 files changed

+1630
-367
lines changed

24 files changed

+1630
-367
lines changed

apps/supervisor/src/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const Env = z.object({
9191
KUBERNETES_MEMORY_REQUEST_RATIO: z.coerce.number().min(0).max(1).default(1), // Ratio of memory limit, so 1 = 100% of memory limit
9292
KUBERNETES_MEMORY_OVERHEAD_GB: z.coerce.number().min(0).optional(), // Optional memory overhead to add to the limit in GB
9393
KUBERNETES_SCHEDULER_NAME: z.string().optional(), // Custom scheduler name for pods
94+
KUBERNETES_LARGE_MACHINE_POOL_LABEL: z.string().optional(), // if set, large-* presets affinity for machinepool=<value>
9495

9596
// Placement tags settings
9697
PLACEMENT_TAGS_ENABLED: BoolEnv.default(false),

apps/supervisor/src/workloadManager/kubernetes.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export class KubernetesWorkloadManager implements WorkloadManager {
9595
},
9696
spec: {
9797
...this.addPlacementTags(this.#defaultPodSpec, opts.placementTags),
98+
affinity: this.#getNodeAffinity(opts.machine),
9899
terminationGracePeriodSeconds: 60 * 60,
99100
containers: [
100101
{
@@ -356,4 +357,55 @@ export class KubernetesWorkloadManager implements WorkloadManager {
356357
},
357358
};
358359
}
360+
361+
#isLargeMachine(preset: MachinePreset): boolean {
362+
return preset.name.startsWith("large-");
363+
}
364+
365+
#getNodeAffinity(preset: MachinePreset): k8s.V1Affinity | undefined {
366+
if (!env.KUBERNETES_LARGE_MACHINE_POOL_LABEL) {
367+
return undefined;
368+
}
369+
370+
if (this.#isLargeMachine(preset)) {
371+
// soft preference for the large-machine pool, falls back to standard if unavailable
372+
return {
373+
nodeAffinity: {
374+
preferredDuringSchedulingIgnoredDuringExecution: [
375+
{
376+
weight: 100,
377+
preference: {
378+
matchExpressions: [
379+
{
380+
key: "node.cluster.x-k8s.io/machinepool",
381+
operator: "In",
382+
values: [env.KUBERNETES_LARGE_MACHINE_POOL_LABEL],
383+
},
384+
],
385+
},
386+
},
387+
],
388+
},
389+
};
390+
}
391+
392+
// not schedulable in the large-machine pool
393+
return {
394+
nodeAffinity: {
395+
requiredDuringSchedulingIgnoredDuringExecution: {
396+
nodeSelectorTerms: [
397+
{
398+
matchExpressions: [
399+
{
400+
key: "node.cluster.x-k8s.io/machinepool",
401+
operator: "NotIn",
402+
values: [env.KUBERNETES_LARGE_MACHINE_POOL_LABEL],
403+
},
404+
],
405+
},
406+
],
407+
},
408+
},
409+
};
410+
}
359411
}

apps/webapp/app/components/code/AIQueryInput.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ interface AIQueryInputProps {
3232
onQueryGenerated: (query: string) => void;
3333
/** Set this to a prompt to auto-populate and immediately submit */
3434
autoSubmitPrompt?: string;
35+
/** Change this to force re-submission even if prompt is the same */
36+
autoSubmitKey?: number;
3537
/** Get the current query in the editor (used for edit mode) */
3638
getCurrentQuery?: () => string;
3739
}
3840

3941
export function AIQueryInput({
4042
onQueryGenerated,
4143
autoSubmitPrompt,
44+
autoSubmitKey,
4245
getCurrentQuery,
4346
}: AIQueryInputProps) {
4447
const [prompt, setPrompt] = useState("");
@@ -50,7 +53,7 @@ export function AIQueryInput({
5053
const [lastResult, setLastResult] = useState<"success" | "error" | null>(null);
5154
const textareaRef = useRef<HTMLTextAreaElement>(null);
5255
const abortControllerRef = useRef<AbortController | null>(null);
53-
const lastAutoSubmitRef = useRef<string | null>(null);
56+
const lastAutoSubmitRef = useRef<{ prompt: string; key?: number } | null>(null);
5457

5558
const organization = useOrganization();
5659
const project = useProject();
@@ -197,19 +200,22 @@ export function AIQueryInput({
197200
[prompt, submitQuery]
198201
);
199202

200-
// Auto-submit when autoSubmitPrompt changes
203+
// Auto-submit when autoSubmitPrompt or autoSubmitKey changes
201204
useEffect(() => {
202-
if (
203-
autoSubmitPrompt &&
204-
autoSubmitPrompt.trim() &&
205-
autoSubmitPrompt !== lastAutoSubmitRef.current &&
206-
!isLoading
207-
) {
208-
lastAutoSubmitRef.current = autoSubmitPrompt;
205+
if (!autoSubmitPrompt || !autoSubmitPrompt.trim() || isLoading) {
206+
return;
207+
}
208+
209+
const last = lastAutoSubmitRef.current;
210+
const isDifferent =
211+
last === null || autoSubmitPrompt !== last.prompt || autoSubmitKey !== last.key;
212+
213+
if (isDifferent) {
214+
lastAutoSubmitRef.current = { prompt: autoSubmitPrompt, key: autoSubmitKey };
209215
setPrompt(autoSubmitPrompt);
210216
submitQuery(autoSubmitPrompt);
211217
}
212-
}, [autoSubmitPrompt, isLoading, submitQuery]);
218+
}, [autoSubmitPrompt, autoSubmitKey, isLoading, submitQuery]);
213219

214220
// Cleanup on unmount
215221
useEffect(() => {

apps/webapp/app/components/code/TSQLEditor.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { sql, StandardSQL } from "@codemirror/lang-sql";
2-
import { autocompletion } from "@codemirror/autocomplete";
2+
import { autocompletion, startCompletion } from "@codemirror/autocomplete";
33
import { linter, lintGutter } from "@codemirror/lint";
4+
import { EditorView } from "@codemirror/view";
45
import type { ViewUpdate } from "@codemirror/view";
56
import { CheckIcon, ClipboardIcon, SparklesIcon, TrashIcon } from "@heroicons/react/20/solid";
67
import {
@@ -103,6 +104,23 @@ export function TSQLEditor(opts: TSQLEditorProps) {
103104
maxRenderedOptions: 50,
104105
})
105106
);
107+
108+
// Trigger autocomplete when ' is typed in value context
109+
// CodeMirror's activateOnTyping only triggers on alphanumeric characters,
110+
// so we manually trigger for quotes after comparison operators
111+
exts.push(
112+
EditorView.domEventHandlers({
113+
keyup: (event, view) => {
114+
// Trigger on quote key (both ' and shift+' on some keyboards)
115+
if (event.key === "'" || event.key === '"' || event.code === "Quote") {
116+
setTimeout(() => {
117+
startCompletion(view);
118+
}, 50);
119+
}
120+
return false;
121+
},
122+
})
123+
);
106124
}
107125

108126
// Add TSQL linter

apps/webapp/app/components/code/TSQLResultsTable.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ function CellValue({
134134
}) {
135135
// Plain text mode - render everything as monospace text with truncation
136136
if (!prettyFormatting) {
137+
if (column.type === "JSON") {
138+
return <JSONCellValue value={value} />;
139+
}
140+
137141
const plainValue = value === null ? "NULL" : String(value);
138142
const isTruncated = plainValue.length > MAX_STRING_DISPLAY_LENGTH;
139143

@@ -277,24 +281,7 @@ function CellValue({
277281

278282
// JSON type
279283
if (type === "JSON") {
280-
const jsonString = JSON.stringify(value);
281-
const isTruncated = jsonString.length > MAX_STRING_DISPLAY_LENGTH;
282-
283-
if (isTruncated) {
284-
return (
285-
<SimpleTooltip
286-
content={
287-
<pre className="max-w-sm whitespace-pre-wrap break-all font-mono text-xs">
288-
{jsonString}
289-
</pre>
290-
}
291-
button={
292-
<span className="font-mono text-xs text-text-dimmed">{truncateString(jsonString)}</span>
293-
}
294-
/>
295-
);
296-
}
297-
return <span className="font-mono text-xs text-text-dimmed">{jsonString}</span>;
284+
return <JSONCellValue value={value} />;
298285
}
299286

300287
// Array types
@@ -382,6 +369,28 @@ function EnvironmentCellValue({ value }: { value: string }) {
382369
return <EnvironmentLabel environment={environment} />;
383370
}
384371

372+
function JSONCellValue({ value }: { value: any }) {
373+
const jsonString = JSON.stringify(value);
374+
const isTruncated = jsonString.length > MAX_STRING_DISPLAY_LENGTH;
375+
376+
if (isTruncated) {
377+
return (
378+
<SimpleTooltip
379+
content={
380+
<pre className="max-w-sm whitespace-pre-wrap break-all font-mono text-xs">
381+
{jsonString}
382+
</pre>
383+
}
384+
button={
385+
<span className="font-mono text-xs text-text-dimmed">{truncateString(jsonString)}</span>
386+
}
387+
/>
388+
);
389+
}
390+
391+
return <span className="font-mono text-xs text-text-dimmed">{jsonString}</span>;
392+
}
393+
385394
/**
386395
* Check if a column should be right-aligned (numeric columns, duration, cost)
387396
*/

0 commit comments

Comments
 (0)