aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data')
-rw-r--r--Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/planner_prompt.txt9
-rw-r--r--Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/ploty_sample.txt126
-rw-r--r--Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/schema.json85
3 files changed, 213 insertions, 7 deletions
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/planner_prompt.txt b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/planner_prompt.txt
index c8557a41f..b68b05755 100644
--- a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/planner_prompt.txt
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/planner_prompt.txt
@@ -23,24 +23,29 @@ SCHEMA USE
- If you are joining and want to project two columns with the same name append '1' to the end of the second table name. (e.g: SitesTable | join kind=inner (OrganizationsTable) on $left.ORGANIZATION_GUID == $right.GUID | project Site = NAME, Organization = NAME1).
- To get machine's organization name example: MachinesTable | where SERIAL_NUMBER == '30001' | join kind=inner (OrganizationsTable) on $left.ORGANIZATION_GUID == $right.GUID | project OrganizationName = NAME1.
- To get machine's site name example: MachinesTable | where SERIAL_NUMBER == '30001' | join kind=inner (SitesTable) on $left.SITE_GUID == $right.GUID | project SiteName = NAME1.
+- Use the LogsTable where Category == "Error" to identify machine errors.
+- When asked to generate a chart or graph, produce KQL that returns ploty output as a result, similar to the provided ploty examples, and classify as "ploty".
+- Use the provided ploty examples as a reference on how to properly design the query and ploty output.
ROUTING
- Classify into exactly one:
- "data": requires querying telemetry via KQL (counts, trends, top-N, rates, timelines, lookups).
+ - "ploty": user asked to generate a chart or graph.
- "docs": architectural/how-to/design/definitions; no live data needed.
- "none": small-talk (“thanks”, “hi”), meta-questions about how you chose a query, or requests to rephrase without needing data.
- Use conversational context from prior turns. If the current question refers to a previous result (“that”, “the same one”, “which organization is that?”), KEEP the same assistant as the previous turn unless the user clearly asks for documentation.
OUTPUT FORMAT — RAW JSON ONLY (no code fences)
Return a single JSON object with these fields:
-- assistant: "data" | "docs" | "none"
-- kql: string ("" when assistant ≠ "data")
+- assistant: "data" | "ploty" | "docs" | "none"
+- kql: string ("" when assistant ≠ "data" or "ploty")
- parameters: object with any needed parameters ({} when assistant ≠ "data"). Use fromDate/toDate when applicable.
- parameterTypes (optional): object (e.g., {"fromDate":"datetime","toDate":"datetime"})
- assumptions: array of short strings (may be empty)
- why: short natural-language rationale (string)
- conversation: string
* When assistant == "none": put a SHORT friendly reply (1–3 sentences) that addresses the user directly without KQL.
+ * When assistant == "ploty": put a title and a description of the chat/graph.
* Otherwise: set to "".
EXAMPLES
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/ploty_sample.txt b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/ploty_sample.txt
new file mode 100644
index 000000000..d4c4e0c42
--- /dev/null
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/ploty_sample.txt
@@ -0,0 +1,126 @@
+--Ploty Examples--
+
+Pie Chart of Job Statuses:
+
+let data =
+JobRunsTable
+ | summarize Completed = countif(Status == "Completed"), Failed = countif(Status == "Failed"), Aborted = countif(Status == "Aborted");
+data
+| extend TotalJobs = Completed + Failed + Aborted
+| extend ploty = strcat(
+ '{"data":[{"type":"pie",',
+ '"labels":["Completed","Failed","Aborted"],',
+ '"values":[', tostring(Completed), ',', tostring(Failed), ',', tostring(Aborted), '],',
+ '"marker":{"colors":["green","red","orange"]},',
+ '"textinfo":"label+value",',
+ '"hoverinfo":"label+value"}],',
+ '"layout":{"title":"Job Status Distribution (', tostring(TotalJobs), ')",',
+ '"showlegend":false}}'
+)
+| project ploty
+
+
+Ink Consumption by Color (L)
+
+JobRunsTable
+| summarize
+ Cyan = sum(OutputCyan),
+ Magenta = sum(OutputMagenta),
+ Yellow = sum(OutputYellow),
+ Black = sum(OutputBlack),
+ LightCyan = sum(OutputLightCyan),
+ LightMagenta = sum(OutputLightMagenta),
+ LightYellow = sum(OutputLightYellow),
+ Blue = sum(OutputBlue),
+ LightBlue = sum(OutputLightBlue),
+ Orange = sum(OutputOrange),
+ LightOrange = sum(OutputLightOrange),
+ Rubine = sum(OutputRubine),
+ LightRubine = sum(OutputLightRubine),
+ Navy = sum(OutputNavy),
+ Violet = sum(OutputViolet),
+ Transparent = sum(OutputTransparent)
+| extend keys = pack_array("Cyan","Magenta","Yellow","Black","Light Cyan","Light Magenta","Light Yellow","Blue","Light Blue","Orange","Light Orange","Rubine","Light Rubine","Navy","Violet","Transparent")
+| extend values = pack_array(
+ toreal(Cyan)/1000000000.0,
+ toreal(Magenta)/1000000000.0,
+ toreal(Yellow)/1000000000.0,
+ toreal(Black)/1000000000.0,
+ toreal(LightCyan)/1000000000.0,
+ toreal(LightMagenta)/1000000000.0,
+ toreal(LightYellow)/1000000000.0,
+ toreal(Blue)/1000000000.0,
+ toreal(LightBlue)/1000000000.0,
+ toreal(Orange)/1000000000.0,
+ toreal(LightOrange)/1000000000.0,
+ toreal(Rubine)/1000000000.0,
+ toreal(LightRubine)/1000000000.0,
+ toreal(Navy)/1000000000.0,
+ toreal(Violet)/1000000000.0,
+ toreal(Transparent)/1000000000.0
+)
+| extend colors = pack_array("#00FFFF", "#FF00FF", "#FFFF00", "#000000", "#E0FFFF", "#FFB6C1", "#FFFFE0", "#0000FF", "#ADD8E6", "#FFA500", "#FFE4B5", "#D2042D", "#FF69B4", "#000080", "#8A2BE2", "#FFFFFF")
+| extend ploty = strcat(
+ '{"data":[{"type":"bar",',
+ '"x":', tostring(keys), ',',
+ '"y":', tostring(values), ',',
+ '"marker":{"color":', tostring(colors), '}',
+ '}],',
+ '"layout":{"title":"Ink Consumption by Color (L)","xaxis":{"title":"Color"},"yaxis":{"title":"Total Ink (L)"}}}'
+)
+| project ploty
+
+
+Failing Machines (TOP 10):
+
+let data = JobRunsTable
+| where Status == "Failed"
+| summarize FailedCount = count() by SerialNumber, Site
+| top 10 by FailedCount desc
+| order by FailedCount desc
+| extend SerialNumberWithSite = strcat(SerialNumber, iif(isnull(Site), "", strcat(" (", Site, ")")));
+let x = toscalar(data | summarize make_list(SerialNumberWithSite));
+let y = toscalar(data | summarize make_list(FailedCount));
+let colors = dynamic([
+ "#B30000", "#CC0000", "#E60000", "#FF0000", "#FF2A2A",
+ "#FF3333", "#FF4D4D", "#FF6666", "#FF9999", "#FFCCCC"
+]);
+print ploty = tostring(strcat(
+ '{"data":[',
+ '{"x":', tostring(x),
+ ',"y":', tostring(y),
+ ',"type":"bar",',
+ '"marker":{"color":', tostring(colors), '}',
+ '}],',
+ '"layout":{"title":"",',
+ '"xaxis":{"title":"Machine (Site)","automargin":true},',
+ '"yaxis":{"title":"Failure Count"},',
+ '"margin":{"b":150}}}'
+))
+
+
+Resume Stats:
+
+let r1="Continuous request message 'JobRequest' had failed to provide a response for a period of 10 seconds and has timed out.";
+let r2="Transporter disconnected.";
+let ids_with_disconnect=JobRunsTable
+| where isnotempty(FailureReason)
+| where FailureReason endswith r1 or FailureReason endswith r2
+| distinct ID;
+let latest_per_id=JobRunsTable
+| summarize arg_max(CreatedTime, *) by ID
+| extend LatestIsDisconnect=isnotempty(FailureReason) and (FailureReason endswith r1 or FailureReason endswith r2);
+let totals=latest_per_id
+| where ID in (ids_with_disconnect)
+| summarize Resumed=countif(not(LatestIsDisconnect)), NotResumed=countif(LatestIsDisconnect);
+totals
+| extend ["Job Runs Recovered By Resume"]=Resumed, ["Job Runs Could not Resume"]=NotResumed, ["Total Disconnections"]=Resumed+NotResumed, ["Success Rate"]=round(100.0*todouble(Resumed)/todouble(Resumed+NotResumed),2)
+| project-away Resumed, NotResumed
+| extend keys=pack_array("Job Runs Recovered By Resume","Job Runs Could not Resume","Total Disconnections","Success Rate"),
+ values=pack_array(toreal(['Job Runs Recovered By Resume']),toreal(['Job Runs Could not Resume']),toreal(['Total Disconnections']),toreal(['Success Rate'])),
+ colors=pack_array("#28a745","#dc3545","#6c757d","#007bff")
+| extend ploty=strcat('{"data":[{"type":"bar","x":', tostring(keys), ',"y":', tostring(values), ',"marker":{"color":', tostring(colors), '}}],"layout":{"title":"Job Resume Analysis","xaxis":{"title":""},"yaxis":{"title":"Value"},"showlegend":false}}')
+
+
+
+--Ploty Examples-- \ No newline at end of file
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/schema.json b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/schema.json
index 41da6e63a..dba20ffb8 100644
--- a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/schema.json
+++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Data/schema.json
@@ -136,17 +136,91 @@
}
]
},
- "JobStatusTable": {
+ "MachineStatusesTable": {
"columns": {
"SerialNumber": "string",
+ "MachineType": "string",
+ "Environment": "string",
+ "Organization": "string",
+ "Site": "string",
"CreatedTime": "datetime",
+ "UploadTime": "datetime",
"State": "string",
"OverallTemperature": "real",
"Status": "dynamic"
},
+
+ "primaryTimeColumn": "CreatedTime",
+ "fallbackTimeColumns": [ "UploadTime" ],
+ "defaultTimeWindowDays": 1,
+
+ "keys": {
+ "machineKey": [ "SerialNumber" ]
+ },
+
+ "enums": {
+ "State": [
+ "PowerUp",
+ "Ready",
+ "Sleep",
+ "Error",
+ "ShuttingDown",
+ "PreparingJob",
+ "RunningJob",
+ "PowerDown"
+ ]
+ },
+
"notes": [
- "High-volume time series of machine status snapshots. Short retention."
- ]
+ "One row per machine status snapshot. Use CreatedTime for analysis; UploadTime is the ingestion/upload timestamp.",
+ "Status is a dynamic JSON object mirroring the on-device status payload (e.g., State, OverallTemperature, SpoolState, IDSPacksLevels[], error flags).",
+ "OverallTemperature is also duplicated at the top level for quick filtering/aggregation; the authoritative nested copy is Status.OverallTemperature.",
+ "Common expansions: Status.State (string), Status.SpoolState (string), Status.IDSPacksLevels (array of per-pack objects with MidTankLevel, DispenserLevel, etc.).",
+ "Example: latest state per machine (last 24h): MachineStatusesTable | where CreatedTime > ago(1d) | summarize arg_max(CreatedTime, *) by SerialNumber | project SerialNumber, Time=CreatedTime, State, OverallTemperature",
+ "Example: detect low MidTankLevel on any pack: MachineStatusesTable | where CreatedTime > ago(1d) | mv-expand pack=Status.IDSPacksLevels | project SerialNumber, CreatedTime, Index=todouble(pack.Index), MidTankLevel=toreal(pack.MidTankLevel) | where MidTankLevel < 2 | summarize min(MidTankLevel), any(CreatedTime) by SerialNumber, Index",
+ "Example: error intervals: MachineStatusesTable | where CreatedTime > ago(7d) | summarize Start=min(CreatedTime), End=max(CreatedTime), Count=count() by SerialNumber, State | where State == \"Error\" | order by Count desc",
+ "Example: Get the overall temperature during the last job for machine 30001: let LastJob = JobRunsTable | where SerialNumber == \"30001\" | summarize arg_max(EndTime, StartTime, EndTime) by SerialNumber; MachineStatusesTable | where SerialNumber == \"30001\" | join kind=inner (LastJob) on SerialNumber | where CreatedTime between (StartTime .. EndTime)"
+ ],
+
+ "guardrails": {
+ "requireTimeFilter": true,
+ "maxRowsSuggested": 500,
+ "encourageSummarizeOrTop": true
+ },
+
+ "joinHints": [
+ {
+ "table": "JobRunsTable",
+ "on": [ "SerialNumber" ],
+ "why": "Correlate state transitions (PreparingJob/RunningJob/Error) with job windows."
+ },
+ {
+ "table": "MachinesTable",
+ "on": [ "SerialNumber" ],
+ "why": "Enrich with machine metadata (organization/site/version)."
+ },
+ {
+ "table": "LogsTable",
+ "on": [ "SerialNumber" ],
+ "why": "Investigate logs around status changes using a time window."
+ }
+ ],
+
+ "display": {
+ "rename": {
+ "CreatedTime": "Time"
+ },
+ "suggestedProjections": [
+ "SerialNumber",
+ "Time",
+ "State",
+ "OverallTemperature",
+ "Status.SpoolState",
+ "Status.IDSPacksLevels"
+ ]
+ }
+
+
},
"MachinesTable": {
"columns": {
@@ -266,7 +340,8 @@
"Application logs emitted by services/apps running on or about the machines.",
"Use CreatedTime for time filters; UploadTime is the ingestion/upload timestamp.",
"Category is limited to Info, Warning, Error, Critical, Debug.",
- "Source can be either Application or Firmware where Firmware represents logs generated by the embedded device and Application is everything else."
+ "Source can be either Application or Firmware where Firmware represents logs generated by the embedded device and Application is everything else.",
+ "Example to get error logs associated with a specific job: let LastJob = JobRunsTable | where SerialNumber == \"30001\" | summarize arg_max(EndTime, StartTime, EndTime) by SerialNumber; LogsTable | join kind=inner (LastJob) on SerialNumber | where Category == \"Error\" | where CreatedTime between (StartTime .. EndTime) | order by CreatedTime asc | project Time = format_datetime(CreatedTime, 'HH:mm:ss.fff'), Message"
],
"guardrails": {
@@ -461,4 +536,4 @@
]
}
}
-} \ No newline at end of file
+}