Browse Source

Task/improve consistency of Ghostfolio AI (#8298)

* Set temperature of models of Ghostfolio AI to 0

* Update changelog
pull/8297/head^2
Thomas Kaul 4 days ago
committed by GitHub
parent
commit
4f17f0ecd4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 11
      apps/api/src/app/endpoints/ai/ai.service.spec.ts
  3. 12
      apps/api/src/app/endpoints/ai/ai.service.ts

6
CHANGELOG.md

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Improved the consistency of the import of activities with _Ghostfolio AI_ (experimental)
## 3.92.0 - 2026-10-24
### Changed

11
apps/api/src/app/endpoints/ai/ai.service.spec.ts

@ -202,7 +202,7 @@ describe('AiService', () => {
);
});
it('Restricts the routing to endpoints with zero data retention, disables the reasoning and passes a PDF file to the model itself', async () => {
it('Restricts the routing to endpoints with zero data retention, disables the reasoning, sets the temperature to 0 and passes a PDF file to the model itself', async () => {
await aiService.getActivityRowsFromFile({
file: { buffer: Buffer.from('%PDF-1.7'), mediaType: 'application/pdf' },
locale: 'de-CH'
@ -214,6 +214,10 @@ describe('AiService', () => {
reasoning: { effort: 'none' },
usage: { include: true }
});
expect(generateText).toHaveBeenCalledWith(
expect.objectContaining({ temperature: 0 })
);
});
it('Falls back to the general model if no vision model is set', async () => {
@ -1524,7 +1528,7 @@ describe('AiService', () => {
);
});
it('Restricts the routing to endpoints with zero data retention, disables the reasoning and gives the model no tools', async () => {
it('Restricts the routing to endpoints with zero data retention, disables the reasoning, sets the temperature to 0 and gives the model no tools', async () => {
mockRowMatches({});
await aiService.getActivitiesFromRows({
@ -1545,7 +1549,8 @@ describe('AiService', () => {
expect.objectContaining({
abortSignal,
model,
output: { schema: ROW_MATCHES_OUTPUT }
output: { schema: ROW_MATCHES_OUTPUT },
temperature: 0
})
);

12
apps/api/src/app/endpoints/ai/ai.service.ts

@ -288,7 +288,8 @@ export class AiService {
* the rows still have to be matched to asset profiles and accounts. The
* name of the file is passed as well, as it can name the broker. The
* reasoning is disabled, as it can add thousands of output tokens even for a
* single row. A row above the maximum number of activities is left out. An
* single row. The temperature is 0, so that the same file gives the same
* rows. A row above the maximum number of activities is left out. An
* output which reaches the maximum number of output tokens is cut, hence it
* gives an error for the caller. A cut output without any text is not parsed
* at all, hence the finish reason is checked as well.
@ -343,7 +344,8 @@ export class AiService {
usage: { include: true }
}
}),
output: Output.object({ schema: ACTIVITY_ROWS_OUTPUT })
output: Output.object({ schema: ACTIVITY_ROWS_OUTPUT }),
temperature: 0
}).catch((error: unknown) => {
if (
NoObjectGeneratedError.isInstance(error) &&
@ -551,7 +553,8 @@ export class AiService {
* the model gets no accounts, so that only the account hints have no match.
* If the holdings or the data sources cannot be got, the model gets no
* holdings. The reasoning is disabled, as it can add thousands of output
* tokens. An asset match is left out if neither the search of its own asset
* tokens. The temperature is 0, so that the same assets give the same
* matches. An asset match is left out if neither the search of its own asset
* nor the holdings gave its asset profile, or if its holding has another ISIN
* than its own asset, as the import refuses a symbol which the data source
* cannot resolve and as the model can mix up the ids of the assets. The ISINs
@ -632,7 +635,8 @@ export class AiService {
assets: assets.map((asset, index) => {
return { ...asset, assetProfiles: searches[index].assetProfiles };
})
})
}),
temperature: 0
});
const assetMatches = output.assets.filter(({ dataSource, id, symbol }) => {

Loading…
Cancel
Save