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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,8 @@ By default, a Smart Connector waits `10` seconds max for a reply from another Sm

*Increasing the HTTP timeouts*

By default, a KER waits `5` seconds max for a HTTP connection response from another KER when sending a message via the inter-KER protocol. The time is configurable via the `ke.http.timeout` property
By default, a KER waits `5` seconds max for a HTTP connection response from another KER when sending a message via the inter-KER protocol. The time is configurable via the `ke.http.timeout` property.

*Configure the reasoner level*

By default, the reasoner level is set to `2`, but can be overridden as described above. The reasoner level (1-5) determines how advanced the reasoner mechanism will be. Every Smart Connector within the Knowledge Engine Runtime will use the configured reasoning level unless specified otherwise. The level can be configured via the `ke.reasoner.level` property.
15 changes: 8 additions & 7 deletions docs/docs/reasoning.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ We can distinguish between two types of reasoning as it happens within the knowl
1. reasoning to infer new data, and
2. reasoning for orchestration of data

It is optional to use the reasoner, and you have to specifically opt in to enable it.
If you don't opt in, the *matcher* is used, which simply only facilitates data exchange when graph patterns match exactly (except for order of triples and variable names).
The reasoner is always enabled, but you can configure the reasoner level with a number between 1 and 5, inclusive. The lower reasoner level facilitates data exchange when graph patterns match exactly (except for order of triples and variable names) and will not combine multiple actors to satisfy the interaction.

## Reasoning to infer new data

Expand Down Expand Up @@ -184,32 +183,34 @@ In `examples/reasoner/` in the Knowledge Engine repository, you can find a compl
That example is a variant on the unit conversion orchestration.


## Enabling the reasoner
## Setting the reasoner level

You can configure the default reasoner level via the `ke.reasoner.level` configuration option. See [configuration](https://github.com/TNO/knowledge-engine?tab=readme-ov-file#configuration) section for more info.

<Tabs groupId="tke-usage">
<TabItem value="java" label="Java">

```java
smartConnector.setReasonerEnabled(true);
smartConnector.setReasonerLevel(4);
```

</TabItem>
<TabItem value="bash" label="Rest API">

When using the REST API, you can enable the reasoner in your smart connector by adding the `reasonerEnabled` property during knowledge base registration:
When using the REST API, you can enable the reasoner in your smart connector by adding the `reasonerLevel` property during knowledge base registration:

```json
{
"knowledgeBaseId": "http://example.org/kb-with-reasoner-enabled",
"knowledgeBaseName": "My reasonable knowledge base",
"knowledgeBaseDescription": "This is an example knowledge base with the reasoner turned on.",
"reasonerEnabled": true
"reasonerLevel": 4
}
```

</TabItem>
</Tabs>
When reasoning is enabled, any **proactive** Knowledge Interaction (i.e. ASK and POST) that you trigger in the smart connector will use the reasoner.
Any **proactive** Knowledge Interaction (i.e. ASK and POST) that you trigger in the smart connector will use the specified reasoner level.


## Performance warning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,43 @@
* extremely slow and often throws out of memory exceptions.
*/
public enum MatchStrategy {
SUPREME_LEVEL, ULTRA_LEVEL, ADVANCED_LEVEL, NORMAL_LEVEL, ENTRY_LEVEL;
/**
* Fastest but finds least matches between rules. Matches cannot consist of
* multiple rules and should also fully cover the target graph pattern.
*/
ENTRY_LEVEL,

/**
* Faster but finds only limited matches between rules. Matches can consist of
* multiple rules, but should still fully cover the target graph pattern which
* means detecting knowledge gaps is not supported.
*/
NORMAL_LEVEL,

/**
* Slower but finds more matches between rules. Matches can consist of multiple
* rules and transitive rules (that refer to themselves) are supported as well,
* but matches should still fully cover the target graph pattern which means
* detecting knowledge gaps is not supported.
*/
ADVANCED_LEVEL,

/**
* Even slower but finds almost all matches between rules. Matches can consist
* of multiple rules and transitive rules (that refer to themselves) are
* supported as well and matches do not need to fully cover the target graph
* pattern and this means knowledge gap detection is supported.
*/
ULTRA_LEVEL,

/**
* Slowest, but finds all possible matches between rules. Matches can consist of
* multiple rules and transitive rules (that refer to themselves) are supported.
* Matches do not need to fully cover the target graph pattern and this means
* knowledge gap detection is supported. This level also finds matches that are
* already encompassed by other matches.
*/
SUPREME_LEVEL;

public EnumSet<MatchFlag> toConfig(boolean antecedentOfTarget) {
EnumSet<MatchFlag> config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,20 +271,22 @@ CompletableFuture<PostResult> post(PostKnowledgeInteraction aPKI, RecipientSelec
void setDomainKnowledge(Set<Rule> someDomainKnowledge);

/**
* Sets the reasoner enabled property of this Smart Connector to true or false.
* Enabling the reasoner causes the data exchange to become more flexible, but
* also causes the data exchange to be slower.
* Sets the default reasoner level of this Smart Connector between 1-5.
* Increasing the level of the reasoner causes the data exchange to become more
* flexible, but also causes the data exchange to be slower.
*
* @param aReasonerEnabled {@code true} if the reasoner should be enabled,
* {@code false} otherwise.
* @param aReasonerLevel The default level of the reasoner. For details on the
* different reasoner levels, see
* {@link SmartConnectorConfig#CONF_KEY_KE_REASONER_LEVEL}.
*/
void setReasonerEnabled(boolean aReasonerEnabled);
void setReasonerLevel(int aReasonerLevel);

/**
* @return {@code true} if this smart connector uses the reasoner for data
* exchange, {@code false} otherwise.
* @return The default reasoner level of this smart connector for data exchange.
* For details on the different reasoner levels, see
* {@link SmartConnectorConfig#CONF_KEY_KE_REASONER_LEVEL}
*/
boolean isReasonerEnabled();
int getReasonerLevel();

/**
* Stops the current {@link SmartConnector}. Note that this methods is
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package eu.knowledge.engine.smartconnector.api;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SmartConnectorConfig {

/**
* The log facility of this class.
*/
public static final Logger LOG = LoggerFactory.getLogger(SmartConnectorConfig.class);

/**
* Key to set whether this KER should strictly validate whether outgoing
* bindings are compatible with incoming bindings.
*/
public static final String CONF_KEY_VALIDATE_OUTGOING_BINDINGS_WRT_INCOMING_BINDINGS = "sc.validate.outgoing.bindings.wrt.incoming.bindings";

/**
* Key to configure the hostname of the machine this Knowledge Engine Runtime
* (KER) runs on.
*
* @deprecated Replaced by
* {@link SmartConnectorConfig#CONF_KEY_KE_RUNTIME_EXPOSED_URL}
*/
@Deprecated
public static final String CONF_KEY_KE_RUNTIME_HOSTNAME = "ke.runtime.hostname";

/**
* Key to configure the URL of the Knowledge Directory where this KER can find
* other KERs in the network. Note that overriding this configuration property
* will run this KER in distributed mode.
*/
public static final String CONF_KEY_KD_URL = "kd.url";

/**
* Key to configure the time in seconds the SCs in this KER wait for a HTTP
* connection response from another KER. Only used in distributed mode.
*/
public static final String CONF_KEY_KE_HTTP_TIMEOUT = "ke.http.timeout";

/**
* Key to configure the how many seconds the MessageRouter should wait for
* ANSWER/REACT Message when sending a ASK/POST Message? 0 means wait forever
* (useful when working with a human KB).
*/
public static final String CONF_KEY_KE_KB_WAIT_TIMEOUT = "ke.kb.wait.timeout";

/**
* Key to configure the URL that is advertised to other KERs. Other KERs can use
* this URL to reach this KER. Note that this configuration property is only
* used in distributed mode.
*/
public static final String CONF_KEY_KE_RUNTIME_EXPOSED_URL = "ke.runtime.exposed.url";

/**
* Key to configure the port at which the KER's peer-to-peer communication
* should happen. Only used in distributed mode.
*/
public static final String CONF_KEY_KE_RUNTIME_PORT = "ke.runtime.port";

/**
* Key to configure the default reasoner level (1-5) that is used in the current
* KER when no reasoner level is provided by the user. The meaning of each
* levels is:
* <ul>
* <li><b>1</b>: Fastest but least thorough level. Configures the matching
* algorithm to {@link MatchStrategy#ENTRY_LEVEL}.</li>
* <li><b>2</b>: Faster but not very thorough level. Configures the matching
* algorithm to {@link MatchStrategy#NORMAL_LEVEL}.</li>
* <li><b>3</b>: Slower but more thorough level. Configures the matching
* algorithm to {@link MatchStrategy#ADVANCED_LEVEL}.</li>
* <li><b>4</b>: Even slower but even more thorough level. Configures the
* matching algorithm to {@link MatchStrategy#ULTRA_LEVEL}.</li>
* <li><b>5</b>: Slowest but most thorough level. Configures the matching
* algorithm to {@link MatchStrategy#SUPREME_LEVEL}.</li>
* </ul>
*/
public static final String CONF_KEY_KE_REASONER_LEVEL = "ke.reasoner.level";

/**
* Convert the configuration reasoner levels to matching strategies used in the
* reasoner code.
*
* @param aReasonerLevel The reasoner level configured in a configuration file.
* @return The corresponding matching strategy belonging to the given reasoner
* level.
*/
public static MatchStrategy toMatchStrategy(int aReasonerLevel) {
MatchStrategy m = null;

switch (aReasonerLevel) {
case 1:
m = MatchStrategy.ENTRY_LEVEL;
break;
case 2:
m = MatchStrategy.NORMAL_LEVEL;
break;
case 3:
m = MatchStrategy.ADVANCED_LEVEL;
break;
case 4:
m = MatchStrategy.ULTRA_LEVEL;
break;
case 5:
m = MatchStrategy.SUPREME_LEVEL;
break;
default:
LOG.warn(
"The configured reasoner level should lie between 1 and 5, inclusive and should not be '{}'. Falling back to reasoner level 1.",
aReasonerLevel);
m = MatchStrategy.ENTRY_LEVEL;
}

return m;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package eu.knowledge.engine.rest.api;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

import eu.knowledge.engine.rest.RestServerHelper;
import eu.knowledge.engine.test_utils.HttpTester;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestReasonerLevel {
private final RestServerHelper rsh = new RestServerHelper();
private static int PORT = 8280;

@BeforeAll
public void setUpServer() {
rsh.start(PORT);
}

@Test
public void testReasonerLevel() throws IOException, InterruptedException {

URL url = new URL("http://localhost:" + PORT + "/rest");

// register the AskKB with out of range reasoner level
var registerKb = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/relationAsker\", \"knowledgeBaseName\": \"RelationAsker\", \"knowledgeBaseDescription\": \"A KB that asks for relations between people\", \"reasonerLevel\" : 0}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerKb.expectStatus(400);

// register the AskKB with out of range reasoner level
registerKb = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/relationAsker\", \"knowledgeBaseName\": \"RelationAsker\", \"knowledgeBaseDescription\": \"A KB that asks for relations between people\", \"reasonerLevel\" : 6}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerKb.expectStatus(400);

// register the AskKB with correct reasoner level
registerKb = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/relationAsker\", \"knowledgeBaseName\": \"RelationAsker\", \"knowledgeBaseDescription\": \"A KB that asks for relations between people\", \"reasonerLevel\" : 2}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerKb.expectStatus(200);

// register the AskKB without reasoner level
var registerKb2 = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/relationAsker2\", \"knowledgeBaseName\": \"RelationAsker\", \"knowledgeBaseDescription\": \"A KB that asks for relations between people\"}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerKb.expectStatus(200);
}

@AfterAll
public void cleanUp() throws MalformedURLException {

TestUtil.unregisterAllKBs("http://localhost:" + PORT + "/rest");
rsh.cleanUp();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ protected boolean removeEldestEntry(Map.Entry<Integer, HandleRequest> eldest) {
}
this.sc = smartConnectorProvider.create(this);

if (scModel.getReasonerEnabled() != null)
this.sc.setReasonerEnabled(scModel.getReasonerEnabled());
if (scModel.getReasonerLevel() != null)
this.sc.setReasonerLevel(scModel.getReasonerLevel());
}

protected void tryProcessHandleRequestElseEnqueue(HandleRequest handleRequest) {
Expand Down Expand Up @@ -396,10 +396,6 @@ public String register(KnowledgeInteractionBase ki) {
}

boolean knowledgeGapsEnabled = ki.getKnowledgeGapsEnabled() == null ? false : ki.getKnowledgeGapsEnabled();
if (knowledgeGapsEnabled && !this.sc.isReasonerEnabled()) {
throw new IllegalArgumentException(
"You can only set knowledgeGapsEnabled when the Knowledge Base is reasonerEnabled.");
}

String type = ki.getKnowledgeInteractionType();
URI kiId;
Expand All @@ -414,6 +410,9 @@ public String register(KnowledgeInteractionBase ki) {
MatchStrategy strategy = null;
if (aki.getKnowledgeGapsEnabled() != null && aki.getKnowledgeGapsEnabled()) {
strategy = MatchStrategy.SUPREME_LEVEL;
LOG.info(
"The MatchStrategy should be '{}' when Knowledge Gaps are enabled. Overriding default.",
MatchStrategy.SUPREME_LEVEL);
}

var askKI = new AskKnowledgeInteraction(ca, new GraphPattern(prefixMapping, aki.getGraphPattern()),
Expand Down Expand Up @@ -512,8 +511,7 @@ public KnowledgeInteractionWithId getKnowledgeInteraction(String knowledgeIntera
return kiToModelKiWithId(kiId, this.knowledgeInteractions.get(kiId));

} catch (URISyntaxException e) {
assert false
: "There should never occur an invalid URI here because it should have been checked in the service implementation.";
assert false : "There should never occur an invalid URI here because it should have been checked in the service implementation.";
}
return null;

Expand Down Expand Up @@ -864,7 +862,7 @@ public boolean isSuspended() {
return this.suspended;
}

public Boolean getReasonerEnabled() {
return this.sc.isReasonerEnabled();
public int getReasonerLevel() {
return this.sc.getReasonerLevel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ public CompletableFuture<Void> createKB(eu.knowledge.engine.rest.model.SmartConn
this.restKnowledgeBases.put(scModel.getKnowledgeBaseId(), new RestKnowledgeBase(scModel, () -> {
f.complete(null);
}));

LOG.info("Added KB {}", scModel.getKnowledgeBaseId());
return f.handle((r, e) -> {

if (r == null && e != null) {
LOG.error("An exception has occured while creating KB ", e);
return null;
Expand Down
Loading