Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 39 additions & 21 deletions aas_editor/tools/handover_doc_llm/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,54 @@
# Handover Documentation tool

The Handover Documentation Tool is a extension of the AAS Manager that allows users to generate a Handover Documentation from a given PDF file with the usage of LLMs.
The Handover Documentation Tool is an extension of the AAS Manager that allows users to generate a Handover Documentation Submodel from a given PDF file with the usage of LLMs.

## Usage

Usage:
- Select the "Handover Documentation tool" from the "Tools" menu.
- In the opened dialog, select the LLM provider and model you want to use.
- Provide your API key for the selected LLM provider.
- For cloud providers: provide your API key. For Ollama: optionally provide a custom base URL.
- Select the embedding provider to use for RAG on large documents. For Ollama embeddings, optionally specify a custom embedding model and base URL.
- Drag and drop or select the PDF file you want to use.
- Check and adjust the extracted data if necessary.
- The Handover Documentation will be added to the current AAS file.
- The Handover Documentation Submodel will be added to the current AAS file or a new file.

The tool uses the [LangChain](https://python.langchain.com/docs/) library for the interaction with LLMs.
It splits the PDF into smaller chunks and uses vector stores to store the chunks.
After that, it uses a RAG chain to query the LLM with the data.
## How it works

The tool uses the [LangChain](https://python.langchain.com/docs/) library for interaction with LLMs.
For large documents it splits the PDF into smaller chunks, stores them in a FAISS vector store, and uses a RAG chain to query the LLM with the relevant context. Small documents are passed directly to the LLM without RAG.

The tool currently supports the following LLM providers:
- OpenAI
- Anthropic
- Google Vertex
- Groq
- Mistral AI
## Supported LLM providers

Disclaimer: The Handover Documentation tool relies on Large Language Models for information extraction. LLMs may produce incomplete, inaccurate, or inconsistent results. Always verify the extracted documentation before use in production or compliance-relevant contexts.
| Provider | Type | Requires |
|----------|------|----------|
| OpenAI | Cloud API | API key |
| Anthropic | Cloud API | API key |
| Google Vertex | Cloud API | API key |
| Groq | Cloud API | API key |
| Mistral AI | Cloud API | API key |
| **Ollama** | **Local** | [Ollama](https://ollama.com) installed & running |

### Using Ollama (local LLMs)

1. Install [Ollama](https://ollama.com) and start the server.
2. Pull a model, e.g.: `ollama pull llama3.2`
3. Select **Ollama** as the LLM provider in the dialog.
4. Enter the model name (e.g. `llama3.2`). Leave the Base URL empty to use the default (`http://localhost:11434`).
5. Select **Ollama** as the embedding provider. Optionally enter a custom embedding model name (default: `nomic-embed-text`) and base URL.
6. Pull the embedding model: `ollama pull nomic-embed-text`

The Handover Documentation Tool is a extension of the AAS Manager that allows users to generate a Handover Documentation Submodel from a given PDF file with the usage of LLMs.
Recommended models for this task: `llama3.2`, `qwen2.5`, `mistral`.

Usage:
- Select the LLM provider and model you want to use.
- Provide your API key for the selected LLM provider.
- Drag and drop or select the PDF file you want to use.
- Check and adjust the extracted data if necessary.
- The Handover Documentation Submodel will be added to the current AAS file or a new file.
## Supported embedding providers

| Provider | Type | Notes |
|----------|------|-------|
| HuggingFace | Local | Default; uses `sentence-transformers/all-mpnet-base-v2`; no key required |
| OpenAI | Cloud API | Requires a separate OpenAI API key |
| Ollama | Local | Custom model (default: `nomic-embed-text`) and base URL configurable |

The embedding provider is selected automatically based on the chosen LLM provider but can be changed manually.

## Disclaimer

The Handover Documentation tool relies on Large Language Models for information extraction. LLMs may produce incomplete, inaccurate, or inconsistent results. Always verify the extracted documentation before use in production or compliance-relevant contexts.
62 changes: 47 additions & 15 deletions aas_editor/tools/handover_doc_llm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,27 @@ def _init_mistral_chat(model, key):
return ChatMistralAI(model=model, api_key=key)


def _init_hf_embeddings(_key):
def _init_ollama_chat(model, key, schema=None):
from langchain_ollama import ChatOllama
base_url = key if key else "http://localhost:11434"
kwargs = {"model": model, "base_url": base_url}
if schema is not None:
kwargs["format"] = schema
return ChatOllama(**kwargs)


def _init_ollama_embeddings(key, model="nomic-embed-text"):
from langchain_ollama import OllamaEmbeddings
base_url = key if key else "http://localhost:11434"
return OllamaEmbeddings(model=model or "nomic-embed-text", base_url=base_url)


def _init_hf_embeddings(_key, _model=None):
from langchain_huggingface import HuggingFaceEmbeddings
return HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")


def _init_openai_embeddings(key):
def _init_openai_embeddings(key, _model=None):
from langchain_openai import OpenAIEmbeddings
return OpenAIEmbeddings(api_key=key)

Expand All @@ -49,7 +64,7 @@ def _init_openai_embeddings(key):

LLM_PROVIDERS = {
"OpenAI": {
"default_model": "gpt-40-mini",
"default_model": "gpt-4o-mini",
"init": _init_openai_chat,
},
"Anthropic": {
Expand All @@ -68,12 +83,25 @@ def _init_openai_embeddings(key):
"default_model": "mistral-large-latest",
"init": _init_mistral_chat,
},
"Ollama": {
"default_model": "llama3.2",
"init": _init_ollama_chat,
},
}

EMBEDDING_PROVIDERS = {
"default": _init_hf_embeddings,
"huggingface": _init_hf_embeddings,
"OpenAI": _init_openai_embeddings,
"HuggingFace": {
"default_model": "",
"init": _init_hf_embeddings,
},
"OpenAI": {
"default_model": "",
"init": _init_openai_embeddings,
},
"Ollama": {
"default_model": "nomic-embed-text",
"init": _init_ollama_embeddings,
},
}

PROMPT = """
Expand Down Expand Up @@ -164,7 +192,10 @@ def _init_openai_embeddings(key):
"required": ["classId"],
"additionalProperties": false,
"properties": {
"classId": { "type": "string", "minLength": 1 }
"classId": {
"type": "string",
"enum": ["01-01", "02-01", "02-02", "02-03", "02-04", "03-01", "03-02", "03-03", "03-04", "03-05", "03-06", "04-01"]
}
}
},
"documentVersion": {
Expand All @@ -176,19 +207,20 @@ def _init_openai_embeddings(key):
],
"additionalProperties": false,
"properties": {
"title": { "type": "object", "additionalProperties": { "type": "string" } },
"subTitle": { "type": "object", "additionalProperties": { "type": "string" } },
"description": { "type": "object", "additionalProperties": { "type": "string" } },
"keyWords": { "type": "object", "additionalProperties": { "type": "string" } },
"version": { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)*$" },
"title": { "type": "object", "minProperties": 1, "additionalProperties": { "type": "string", "minLength": 1 } },
"subTitle": { "type": "object", "additionalProperties": { "type": "string", "minLength": 1 } },
"description": { "type": "object", "minProperties": 1, "additionalProperties": { "type": "string", "minLength": 1 } },
"keyWords": { "type": "object", "additionalProperties": { "type": "string", "minLength": 1 } },
"version": { "type": "string", "minLength": 1 },
"language": {
"type": "array",
"items": { "type": "string", "minLength": 2 },
"items": { "type": "string", "minLength": 2, "maxLength": 3, "pattern": "^[a-z]{2,3}$" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
},
"statusSetDate": { "type": "string", "format": "date-time" },
"statusValue": { "type": "string", "minLength": 1 },
"statusSetDate": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
"statusValue": { "type": "string", "enum": ["InReview", "Released"] },
"organizationShortName": { "type": "string", "minLength": 1 },
"organizationOfficialName": { "type": "string", "minLength": 1 }
}
Expand Down
14 changes: 7 additions & 7 deletions aas_editor/tools/handover_doc_llm/documentation_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
}
}

def json2document(json_str: str):
def json2document(json_str: str, filename: str = None):
"""
Convert a JSON string to documents and entities.

Expand All @@ -122,9 +122,9 @@ def json2document(json_str: str):
from aas_editor.tools.handover_doc_llm.handover_submodel import HandoverDocumentation

documentID = HandoverDocumentation.Documents.Documents_item.DocumentIds.Documentids_item(
documentIsPrimary=True, #TODO: Fix me
documentIsPrimary=True,
documentDomainId=obj['document']['documentId'].get('documentDomainId', ''),
documentIdentifier=obj['document']['documentId'].get('valueId', ''))
documentIdentifier=obj['document']['documentId'].get('documentIdentifier', ''))

classId = obj['document']['documentClassification'].get('classId', '')
documentClassification = HandoverDocumentation.Documents.Documents_item.DocumentClassifications.Documentclassifications_item(
Expand All @@ -134,7 +134,7 @@ def json2document(json_str: str):
)

digitalFile = HandoverDocumentation.Documents.Documents_item.DocumentVersions.Documentversions_item.DigitalFiles.Digitalfiles_item(
value="SOME_PATH", #TODO: Fix me
value=filename,
content_type=MIME_TYPE)

statusSetDate = obj['document']['documentVersion'].get('statusSetDate', '').split('-')
Expand All @@ -146,14 +146,14 @@ def json2document(json_str: str):

documentVersion = HandoverDocumentation.Documents.Documents_item.DocumentVersions.Documentversions_item(
language=obj['document']['documentVersion'].get('language', ['']),
version=obj['document']['documentVersion'].get('documentVersionId', ''),
version=obj['document']['documentVersion'].get('version', ''),
title=obj['document']['documentVersion'].get('title'),
subtitle=obj['document']['documentVersion'].get('subTitle'),
description_=obj['document']['documentVersion'].get('description'),
keyWords=obj['document']['documentVersion'].get('keyWords'),
statusSetDate=statusSetDate,
statusValue=obj['document']['documentVersion'].get('statusValue', ''),
organizationShortName=obj['document']['documentVersion'].get('organizationName', ''),
organizationShortName=obj['document']['documentVersion'].get('organizationShortName', ''),
organizationOfficialName=obj['document']['documentVersion'].get('organizationOfficialName', ''),
digitalFiles=HandoverDocumentation.Documents.Documents_item.DocumentVersions.Documentversions_item.DigitalFiles([digitalFile])
)
Expand Down Expand Up @@ -205,7 +205,7 @@ def documents2handover_documentation(documents, id_: str = "0"):
"title": {"de":"Datensheet", "en":"Datasheet"},
"subTitle": {},
"description": {"de": "Datensheet für 222-101", "en":"Datasheet for 222-101"},
"keyWords": {"de": ["example_company", "222-101"], "en": ["example_company", "222-101"]},
"keyWords": {"de": "222-101", "en": "222-101"},
"statusSetDate": "2025-09-22",
"statusValue": "Released",
"organizationShortName": "example_company",
Expand Down
Loading
Loading