feat: snip a region into chat, and make links clickable (#14)
This commit was merged in pull request #14.
This commit is contained in:
+128
-2
@@ -225,13 +225,139 @@ describe("local tools", () => {
|
||||
expect(result.content).toContain("Current date and time:");
|
||||
});
|
||||
|
||||
it("describes location as a region rather than an address", async () => {
|
||||
it("describes location as a region rather than an address when nothing is configured", async () => {
|
||||
const result = await executeTool("get_location", {}, context);
|
||||
|
||||
expect(result.isError).toBe(false);
|
||||
expect(result.content).toContain("Timezone:");
|
||||
expect(result.content).toContain("Locale:");
|
||||
expect(result.content).toContain("not a precise address");
|
||||
expect(result.content).toContain("no Location is set in preferences");
|
||||
expect(result.content).toContain("Ask which city they are in");
|
||||
});
|
||||
|
||||
it("never claims a city derived from the timezone name", async () => {
|
||||
// `Asia/Manila` is one zone covering the whole Philippines, so reporting
|
||||
// "City: Manila" was wrong for anyone in Cebu and confidently so.
|
||||
const result = await executeTool("get_location", {}, context);
|
||||
|
||||
expect(result.content).not.toMatch(/^City:/m);
|
||||
expect(result.content).toContain("The timezone area is the timezone's own name, not the user's city.");
|
||||
});
|
||||
|
||||
it("puts the configured place first and says to prefer it", async () => {
|
||||
const result = await executeTool("get_location", {}, {
|
||||
...context,
|
||||
place: "Cebu City, Philippines",
|
||||
});
|
||||
|
||||
expect(result.content).toContain("Place: Cebu City, Philippines");
|
||||
expect(result.content).toContain("prefer it");
|
||||
expect(result.content).not.toContain("no Location is set");
|
||||
expect(result.content.indexOf("Place:")).toBeLessThan(result.content.indexOf("Timezone:"));
|
||||
});
|
||||
|
||||
it("still reports the timezone alongside a configured place, for local time questions", async () => {
|
||||
const result = await executeTool("get_location", {}, { ...context, place: "Cebu City" });
|
||||
|
||||
expect(result.content).toContain("Timezone:");
|
||||
expect(result.content).toContain("UTC offset:");
|
||||
});
|
||||
});
|
||||
|
||||
describe("get_location with IP geolocation", () => {
|
||||
const resolved = {
|
||||
status: "ok" as const,
|
||||
place: {
|
||||
place: "Cebu City, Central Visayas, Philippines",
|
||||
city: "Cebu City",
|
||||
region: "Central Visayas",
|
||||
country: "Philippines",
|
||||
timezone: "Asia/Manila",
|
||||
source: "ipwho.is",
|
||||
fetchedAt: 1_000,
|
||||
},
|
||||
};
|
||||
|
||||
it("reports the resolved place and says how approximate it is", async () => {
|
||||
const ipPlace = vi.fn(async () => resolved);
|
||||
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
expect(result.isError).toBe(false);
|
||||
expect(result.content).toContain("Place: Cebu City, Central Visayas, Philippines");
|
||||
expect(result.content).toContain("rough guess");
|
||||
expect(result.content).toContain("ipwho.is");
|
||||
});
|
||||
|
||||
it("does not look the address up at all when the user has said where they are", async () => {
|
||||
// The preference is both more accurate and free, so sending the IP anyway
|
||||
// would be a privacy cost for nothing.
|
||||
const ipPlace = vi.fn(async () => resolved);
|
||||
|
||||
const result = await executeTool("get_location", {}, {
|
||||
...context,
|
||||
place: "Cebu City, Philippines",
|
||||
ipPlace,
|
||||
});
|
||||
|
||||
expect(ipPlace).not.toHaveBeenCalled();
|
||||
expect(result.content).toContain("Place: Cebu City, Philippines");
|
||||
expect(result.content).not.toContain("ipwho.is");
|
||||
});
|
||||
|
||||
it("surfaces a failed lookup instead of quietly serving the region answer", async () => {
|
||||
const ipPlace = vi.fn(async () => ({ status: "failed" as const, message: "ipwho.is answered HTTP 429" }));
|
||||
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
expect(result.content).toContain("IP geolocation is switched on but the lookup failed");
|
||||
expect(result.content).toContain("HTTP 429");
|
||||
// The region answer is still there, so the tool stays useful.
|
||||
expect(result.content).toContain("Timezone:");
|
||||
});
|
||||
|
||||
it("does not blame an unset preference when the lookup itself failed", async () => {
|
||||
// These are different problems and send the user to different places. The
|
||||
// original wording said "no Location is set in preferences" for both, which
|
||||
// is exactly what made a broken lookup look like a missing setting.
|
||||
const ipPlace = vi.fn(async () => ({ status: "failed" as const, message: "ipwho.is answered HTTP 429" }));
|
||||
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
expect(result.content).not.toContain("no Location is set in preferences");
|
||||
});
|
||||
|
||||
it("tells the model to pass a failed lookup on, since it is the only channel to the user", async () => {
|
||||
const ipPlace = vi.fn(async () => ({ status: "failed" as const, message: "offline" }));
|
||||
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
expect(result.content).toContain("Tell the user");
|
||||
expect(result.content).toContain("Setting Location in preferences avoids the lookup");
|
||||
});
|
||||
|
||||
it("flags an IP timezone that disagrees with the clock, which usually means a VPN", async () => {
|
||||
const ipPlace = vi.fn(async () => ({
|
||||
status: "ok" as const,
|
||||
place: { ...resolved.place, timezone: "America/New_York" },
|
||||
}));
|
||||
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
expect(result.content).toContain("differs from the device timezone");
|
||||
});
|
||||
|
||||
it("says nothing about a mismatch when the timezones agree", async () => {
|
||||
const ipPlace = vi.fn(async () => resolved);
|
||||
const result = await executeTool("get_location", {}, { ...context, ipPlace });
|
||||
|
||||
// The test host's own zone varies, so this asserts the absence of the note
|
||||
// only for the case where the provider echoed the same value.
|
||||
if (result.content.includes("IP-reported timezone")) {
|
||||
expect(result.content).toContain("differs");
|
||||
} else {
|
||||
expect(result.content).not.toContain("IP-reported timezone");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user